// Copyright (c) 2021 The Chromium Embedded Framework Authors. // Portions copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "libcef/browser/simple_menu_model_impl.h" #include #include "libcef/browser/thread_util.h" #include "libcef/common/task_runner_impl.h" namespace { // Value based on the documentation on SimpleMenuModel::GetIndexOfCommandId(). const int kInvalidIndex = -1; const int kInvalidCommandId = -1; const int kInvalidGroupId = -1; cef_menu_item_type_t GetCefItemType(ui::MenuModel::ItemType type) { switch (type) { case ui::MenuModel::TYPE_COMMAND: return MENUITEMTYPE_COMMAND; case ui::MenuModel::TYPE_CHECK: return MENUITEMTYPE_CHECK; case ui::MenuModel::TYPE_RADIO: return MENUITEMTYPE_RADIO; case ui::MenuModel::TYPE_SEPARATOR: return MENUITEMTYPE_SEPARATOR; case ui::MenuModel::TYPE_SUBMENU: return MENUITEMTYPE_SUBMENU; default: return MENUITEMTYPE_NONE; } } } // namespace CefSimpleMenuModelImpl::CefSimpleMenuModelImpl( ui::SimpleMenuModel* model, ui::SimpleMenuModel::Delegate* delegate, StateDelegate* state_delegate, bool is_owned, bool is_submenu) : supported_thread_id_(base::PlatformThread::CurrentId()), model_(model), delegate_(delegate), state_delegate_(state_delegate), is_owned_(is_owned), is_submenu_(is_submenu) { DCHECK(model_); DCHECK(delegate_); DCHECK(state_delegate_); } CefSimpleMenuModelImpl::~CefSimpleMenuModelImpl() { // Detach() must be called before object destruction. DCHECK(!model_); DCHECK(submenumap_.empty()); } void CefSimpleMenuModelImpl::Detach() { DCHECK(VerifyContext()); if (!submenumap_.empty()) { auto it = submenumap_.begin(); for (; it != submenumap_.end(); ++it) { it->second->Detach(); } submenumap_.clear(); } if (is_owned_) delete model_; model_ = nullptr; } bool CefSimpleMenuModelImpl::IsSubMenu() { if (!VerifyContext()) return false; return is_submenu_; } bool CefSimpleMenuModelImpl::Clear() { if (!VerifyContext()) return false; model_->Clear(); return true; } int CefSimpleMenuModelImpl::GetCount() { if (!VerifyContext()) return 0; return model_->GetItemCount(); } bool CefSimpleMenuModelImpl::AddSeparator() { if (!VerifyContext()) return false; model_->AddSeparator(ui::NORMAL_SEPARATOR); return true; } bool CefSimpleMenuModelImpl::AddItem(int command_id, const CefString& label) { if (!VerifyContext()) return false; model_->AddItem(command_id, label); return true; } bool CefSimpleMenuModelImpl::AddCheckItem(int command_id, const CefString& label) { if (!VerifyContext()) return false; model_->AddCheckItem(command_id, label); return true; } bool CefSimpleMenuModelImpl::AddRadioItem(int command_id, const CefString& label, int group_id) { if (!VerifyContext()) return false; model_->AddRadioItem(command_id, label, group_id); return true; } CefRefPtr CefSimpleMenuModelImpl::AddSubMenu( int command_id, const CefString& label) { if (!VerifyContext()) return nullptr; auto new_menu = CreateNewSubMenu(nullptr); model_->AddSubMenu(command_id, label, new_menu->model()); return new_menu; } bool CefSimpleMenuModelImpl::InsertSeparatorAt(int index) { if (!VerifyContext()) return false; model_->InsertSeparatorAt(index, ui::NORMAL_SEPARATOR); return true; } bool CefSimpleMenuModelImpl::InsertItemAt(int index, int command_id, const CefString& label) { if (!VerifyContext() || !ValidIndex(index)) return false; model_->InsertItemAt(index, command_id, label); return true; } bool CefSimpleMenuModelImpl::InsertCheckItemAt(int index, int command_id, const CefString& label) { if (!VerifyContext() || !ValidIndex(index)) return false; model_->InsertCheckItemAt(index, command_id, label); return true; } bool CefSimpleMenuModelImpl::InsertRadioItemAt(int index, int command_id, const CefString& label, int group_id) { if (!VerifyContext() || !ValidIndex(index)) return false; model_->InsertRadioItemAt(index, command_id, label, group_id); return true; } CefRefPtr CefSimpleMenuModelImpl::InsertSubMenuAt( int index, int command_id, const CefString& label) { if (!VerifyContext() || !ValidIndex(index)) return nullptr; auto new_menu = CreateNewSubMenu(nullptr); model_->InsertSubMenuAt(index, command_id, label, new_menu->model()); return new_menu; } bool CefSimpleMenuModelImpl::Remove(int command_id) { return RemoveAt(GetIndexOf(command_id)); } bool CefSimpleMenuModelImpl::RemoveAt(int index) { if (!VerifyContext() || !ValidIndex(index)) return false; auto* sub_menu = static_cast(model_->GetSubmenuModelAt(index)); if (sub_menu) { auto it = submenumap_.find(sub_menu); if (it != submenumap_.end()) { it->second->Detach(); submenumap_.erase(it); } } model_->RemoveItemAt(index); return true; } int CefSimpleMenuModelImpl::GetIndexOf(int command_id) { if (!VerifyContext()) return kInvalidIndex; return model_->GetIndexOfCommandId(command_id); } int CefSimpleMenuModelImpl::GetCommandIdAt(int index) { if (!VerifyContext() || !ValidIndex(index)) return kInvalidCommandId; return model_->GetCommandIdAt(index); } bool CefSimpleMenuModelImpl::SetCommandIdAt(int index, int command_id) { NOTIMPLEMENTED(); return false; } CefString CefSimpleMenuModelImpl::GetLabel(int command_id) { return GetLabelAt(GetIndexOf(command_id)); } CefString CefSimpleMenuModelImpl::GetLabelAt(int index) { if (!VerifyContext() || !ValidIndex(index)) return CefString(); return model_->GetLabelAt(index); } bool CefSimpleMenuModelImpl::SetLabel(int command_id, const CefString& label) { return SetLabelAt(GetIndexOf(command_id), label); } bool CefSimpleMenuModelImpl::SetLabelAt(int index, const CefString& label) { if (!VerifyContext() || !ValidIndex(index)) return false; model_->SetLabel(index, label); return true; } CefSimpleMenuModelImpl::MenuItemType CefSimpleMenuModelImpl::GetType( int command_id) { return GetTypeAt(GetIndexOf(command_id)); } CefSimpleMenuModelImpl::MenuItemType CefSimpleMenuModelImpl::GetTypeAt( int index) { if (!VerifyContext() || !ValidIndex(index)) return MENUITEMTYPE_NONE; return GetCefItemType(model_->GetTypeAt(index)); } int CefSimpleMenuModelImpl::GetGroupId(int command_id) { return GetGroupIdAt(GetIndexOf(command_id)); } int CefSimpleMenuModelImpl::GetGroupIdAt(int index) { if (!VerifyContext() || !ValidIndex(index)) return kInvalidGroupId; return model_->GetGroupIdAt(index); } bool CefSimpleMenuModelImpl::SetGroupId(int command_id, int group_id) { return SetGroupIdAt(GetIndexOf(command_id), group_id); } bool CefSimpleMenuModelImpl::SetGroupIdAt(int index, int group_id) { NOTIMPLEMENTED(); return false; } CefRefPtr CefSimpleMenuModelImpl::GetSubMenu(int command_id) { return GetSubMenuAt(GetIndexOf(command_id)); } CefRefPtr CefSimpleMenuModelImpl::GetSubMenuAt(int index) { if (!VerifyContext() || !ValidIndex(index)) return nullptr; auto* sub_model = static_cast(model_->GetSubmenuModelAt(index)); auto it = submenumap_.find(sub_model); if (it != submenumap_.end()) return it->second; return CreateNewSubMenu(sub_model); } bool CefSimpleMenuModelImpl::IsVisible(int command_id) { return IsVisibleAt(GetIndexOf(command_id)); } bool CefSimpleMenuModelImpl::IsVisibleAt(int index) { if (!VerifyContext() || !ValidIndex(index)) return false; return model_->IsVisibleAt(index); } bool CefSimpleMenuModelImpl::SetVisible(int command_id, bool visible) { return SetVisibleAt(GetIndexOf(command_id), visible); } bool CefSimpleMenuModelImpl::SetVisibleAt(int index, bool visible) { if (!VerifyContext() || !ValidIndex(index)) return false; model_->SetVisibleAt(index, visible); return true; } bool CefSimpleMenuModelImpl::IsEnabled(int command_id) { return IsEnabledAt(GetIndexOf(command_id)); } bool CefSimpleMenuModelImpl::IsEnabledAt(int index) { if (!VerifyContext() || !ValidIndex(index)) return false; return model_->IsEnabledAt(index); } bool CefSimpleMenuModelImpl::SetEnabled(int command_id, bool enabled) { return SetEnabledAt(GetIndexOf(command_id), enabled); } bool CefSimpleMenuModelImpl::SetEnabledAt(int index, bool enabled) { if (!VerifyContext() || !ValidIndex(index)) return false; model_->SetEnabledAt(index, enabled); return true; } bool CefSimpleMenuModelImpl::IsChecked(int command_id) { return IsCheckedAt(GetIndexOf(command_id)); } bool CefSimpleMenuModelImpl::IsCheckedAt(int index) { if (!VerifyContext() || !ValidIndex(index)) return false; return model_->IsItemCheckedAt(index); } bool CefSimpleMenuModelImpl::SetChecked(int command_id, bool checked) { if (!VerifyContext() || command_id == kInvalidIndex) return false; state_delegate_->SetChecked(command_id, checked); return true; } bool CefSimpleMenuModelImpl::SetCheckedAt(int index, bool checked) { return SetChecked(GetCommandIdAt(index), checked); } bool CefSimpleMenuModelImpl::HasAccelerator(int command_id) { return HasAcceleratorAt(GetIndexOf(command_id)); } bool CefSimpleMenuModelImpl::HasAcceleratorAt(int index) { if (!VerifyContext() || !ValidIndex(index)) return false; ui::Accelerator accelerator; return model_->GetAcceleratorAt(index, &accelerator); } bool CefSimpleMenuModelImpl::SetAccelerator(int command_id, int key_code, bool shift_pressed, bool ctrl_pressed, bool alt_pressed) { if (!VerifyContext() || command_id == kInvalidIndex) return false; int modifiers = 0; if (shift_pressed) modifiers |= ui::EF_SHIFT_DOWN; if (ctrl_pressed) modifiers |= ui::EF_CONTROL_DOWN; if (alt_pressed) modifiers |= ui::EF_ALT_DOWN; state_delegate_->SetAccelerator( command_id, ui::Accelerator(static_cast(key_code), modifiers)); return true; } bool CefSimpleMenuModelImpl::SetAcceleratorAt(int index, int key_code, bool shift_pressed, bool ctrl_pressed, bool alt_pressed) { return SetAccelerator(GetCommandIdAt(index), key_code, shift_pressed, ctrl_pressed, alt_pressed); } bool CefSimpleMenuModelImpl::RemoveAccelerator(int command_id) { if (!VerifyContext() || command_id == kInvalidIndex) return false; state_delegate_->SetAccelerator(command_id, absl::nullopt); return true; } bool CefSimpleMenuModelImpl::RemoveAcceleratorAt(int index) { return RemoveAccelerator(GetCommandIdAt(index)); } bool CefSimpleMenuModelImpl::GetAccelerator(int command_id, int& key_code, bool& shift_pressed, bool& ctrl_pressed, bool& alt_pressed) { return GetAcceleratorAt(GetIndexOf(command_id), key_code, shift_pressed, ctrl_pressed, alt_pressed); } bool CefSimpleMenuModelImpl::GetAcceleratorAt(int index, int& key_code, bool& shift_pressed, bool& ctrl_pressed, bool& alt_pressed) { if (!VerifyContext() || !ValidIndex(index)) return false; ui::Accelerator accel; if (model_->GetAcceleratorAt(index, &accel)) { key_code = accel.key_code(); shift_pressed = accel.modifiers() & ui::EF_SHIFT_DOWN; ctrl_pressed = accel.modifiers() & ui::EF_CONTROL_DOWN; alt_pressed = accel.modifiers() & ui::EF_ALT_DOWN; return true; } return false; } bool CefSimpleMenuModelImpl::SetColor(int command_id, cef_menu_color_type_t color_type, cef_color_t color) { NOTIMPLEMENTED(); return false; } bool CefSimpleMenuModelImpl::SetColorAt(int index, cef_menu_color_type_t color_type, cef_color_t color) { NOTIMPLEMENTED(); return false; } bool CefSimpleMenuModelImpl::GetColor(int command_id, cef_menu_color_type_t color_type, cef_color_t& color) { NOTIMPLEMENTED(); return false; } bool CefSimpleMenuModelImpl::GetColorAt(int index, cef_menu_color_type_t color_type, cef_color_t& color) { NOTIMPLEMENTED(); return false; } bool CefSimpleMenuModelImpl::SetFontList(int command_id, const CefString& font_list) { NOTIMPLEMENTED(); return false; } bool CefSimpleMenuModelImpl::SetFontListAt(int index, const CefString& font_list) { NOTIMPLEMENTED(); return false; } bool CefSimpleMenuModelImpl::VerifyContext() { if (base::PlatformThread::CurrentId() != supported_thread_id_) { // This object should only be accessed from the thread that created it. NOTREACHED(); return false; } if (!model_) return false; return true; } bool CefSimpleMenuModelImpl::ValidIndex(int index) { return index > kInvalidIndex && index < model_->GetItemCount(); } CefRefPtr CefSimpleMenuModelImpl::CreateNewSubMenu( ui::SimpleMenuModel* model) { bool is_owned = false; if (!model) { model = new ui::SimpleMenuModel(delegate_); is_owned = true; } CefRefPtr new_impl = new CefSimpleMenuModelImpl( model, delegate_, state_delegate_, is_owned, /*is_submodel=*/true); submenumap_.insert(std::make_pair(model, new_impl)); return new_impl; }