remote/handlers/RemoteContextMenuHandler.cpp (169 lines of code) (raw):

#include "RemoteContextMenuHandler.h" #include "../browser/RemoteFrame.h" #include "../callback/RemoteCefRunContextMenuCallback.h" #include "../browser/RemoteBrowser.h" #include <vector> namespace { thrift_codegen::ContextMenuParams convertParams( const CefRefPtr<CefContextMenuParams>& params) { thrift_codegen::ContextMenuParams thriftParams; if (!params) { return thriftParams; } thriftParams.x = params->GetXCoord(); thriftParams.y = params->GetYCoord(); thriftParams.link_url = params->GetLinkUrl().ToString(); thriftParams.unfiltered_link_url = params->GetUnfilteredLinkUrl().ToString(); thriftParams.source_url = params->GetSourceUrl().ToString(); thriftParams.page_url = params->GetPageUrl().ToString(); thriftParams.frame_url = params->GetFrameUrl().ToString(); thriftParams.frame_charset = params->GetFrameCharset(); thriftParams.media_type = static_cast<int>(params->GetMediaType()); thriftParams.media_state_flags = params->GetMediaStateFlags(); thriftParams.selected_text = params->GetSelectionText().ToString(); thriftParams.misspelled_word = params->GetMisspelledWord().ToString(); // not supported: params->GetDictionarySuggestions() thriftParams.is_editable = params->IsEditable(); thriftParams.edit_state_flags = params->GetEditStateFlags(); thriftParams.is_custom_menu = params->IsCustomMenu(); thriftParams.type_flags = params->GetTypeFlags(); thriftParams.has_image_contents = params->HasImageContents(); return thriftParams; } thrift_codegen::MenuItemType::type convert_menu_type( CefMenuModel::MenuItemType type) { switch (type) { case CefMenuModel::MenuItemType::MENUITEMTYPE_NONE: return thrift_codegen::MenuItemType::MENUITEMTYPE_NONE; case CefMenuModel::MenuItemType::MENUITEMTYPE_COMMAND: return thrift_codegen::MenuItemType::MENUITEMTYPE_COMMAND; case CefMenuModel::MenuItemType::MENUITEMTYPE_CHECK: return thrift_codegen::MenuItemType::MENUITEMTYPE_CHECK; case CefMenuModel::MenuItemType::MENUITEMTYPE_RADIO: return thrift_codegen::MenuItemType::MENUITEMTYPE_RADIO; case CefMenuModel::MenuItemType::MENUITEMTYPE_SEPARATOR: return thrift_codegen::MenuItemType::MENUITEMTYPE_SEPARATOR; case CefMenuModel::MenuItemType::MENUITEMTYPE_SUBMENU: return thrift_codegen::MenuItemType::MENUITEMTYPE_SUBMENU; default: LOG(ERROR) << "RemoteContextMenuHandler: Unknown cef menu item type: " << type; return thrift_codegen::MenuItemType::MENUITEMTYPE_NONE; } } /* TODO: Non-recursive DFS std::vector<thrift_codegen::MenuItem> to_thrift(CefRefPtr<CefMenuModel> model) { std::vector<thrift_codegen::MenuItem> result; std::stack<std::pair<CefRefPtr<CefMenuModel>, std::vector<thrift_codegen::MenuItem>*>> stack; // Initialize stack with the root menu model and output list stack.push({model, &result}); while (!stack.empty()) { auto [current_model, current_result] = stack.top(); stack.pop(); for (int i = 0; i < current_model->GetCount(); ++i) { thrift_codegen::MenuItem item; item.label = current_model->GetLabelAt(i).ToString(); item.type = convert_menu_type(current_model->GetTypeAt(i)); item.command_id = current_model->GetCommandIdAt(i); item.group_id = current_model->GetGroupIdAt(i); item.visible = current_model->IsVisibleAt(i); item.enabled = current_model->IsEnabledAt(i); item.checked = current_model->IsCheckedAt(i); // If the current item is a submenu, prepare to process it later if (item.type == thrift_codegen::MenuItemType::MENUITEMTYPE_SUBMENU) { // Submenu items are placed in `item.sub_menu`, which is processed separately item.sub_menu.emplace(); // Initialize sub_menu stack.push({current_model->GetSubMenuAt(i), &item.sub_menu.value()}); } current_result->push_back(std::move(item)); } } return result; } */ std::vector<thrift_codegen::MenuItem> to_thrift(CefRefPtr<CefMenuModel> model) { std::vector<thrift_codegen::MenuItem> result; for (int i = 0; i < model->GetCount(); ++i) { result.emplace_back(); result.back().label = model->GetLabelAt(i).ToString(); result.back().type = convert_menu_type(model->GetTypeAt(i)); result.back().command_id = model->GetCommandIdAt(i); result.back().group_id = model->GetGroupIdAt(i); result.back().visible = model->IsVisibleAt(i); result.back().enabled = model->IsEnabledAt(i); result.back().checked = model->IsCheckedAt(i); result.back().command_id = model->GetCommandIdAt(i); if (result.back().type == thrift_codegen::MenuItemType::MENUITEMTYPE_SUBMENU) { result.back().sub_menu = to_thrift(model->GetSubMenuAt(i)); } } return result; } void to_cef(CefRefPtr<CefMenuModel> out, const std::vector<thrift_codegen::MenuItem>& in) { out->Clear(); for (size_t i = 0; i < in.size(); ++i) { switch (in[i].type) { case thrift_codegen::MenuItemType::MENUITEMTYPE_COMMAND: out->AddItem(in[i].command_id, in[i].label); break; case thrift_codegen::MenuItemType::MENUITEMTYPE_CHECK: out->AddCheckItem(in[i].command_id, in[i].label); out->SetCheckedAt(i, in[i].checked); break; case thrift_codegen::MenuItemType::MENUITEMTYPE_RADIO: out->AddRadioItem(in[i].command_id, in[i].label, in[i].group_id); out->SetCheckedAt(i, in[i].checked); break; case thrift_codegen::MenuItemType::MENUITEMTYPE_SEPARATOR: out->AddSeparator(); break; case thrift_codegen::MenuItemType::MENUITEMTYPE_SUBMENU: { auto sub_menu = out->AddSubMenu(in[i].command_id, in[i].label); to_cef(sub_menu, in[i].sub_menu); break; } case thrift_codegen::MenuItemType::MENUITEMTYPE_NONE: continue; } out->SetEnabledAt(i, in[i].enabled); out->SetVisibleAt(i, in[i].visible); } } } // namespace RemoteContextMenuHandler::RemoteContextMenuHandler(const std::shared_ptr<RpcExecutor>& my_service) : myService(my_service) {} RemoteContextMenuHandler::~RemoteContextMenuHandler() = default; void RemoteContextMenuHandler::OnBeforeContextMenu( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefContextMenuParams> params, CefRefPtr<CefMenuModel> model) { FIND_BID_OR_RETURN(); RemoteFrame::Holder frm(frame); const auto thriftParams = convertParams(params); const std::vector<thrift_codegen::MenuItem> menu_model = to_thrift(model); std::vector<thrift_codegen::MenuItem> result; myService->exec([&](const JavaService& s) { s->ContextMenuHandler_OnBeforeContextMenu( result, bid, frm.serverId(), thriftParams, menu_model); }); to_cef(model, result); } bool RemoteContextMenuHandler::RunContextMenu( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefContextMenuParams> params, CefRefPtr<CefMenuModel> model, CefRefPtr<CefRunContextMenuCallback> callback) { FIND_BID_OR_RETURN_VAL(false); RemoteFrame::Holder frm(frame); const auto thriftParams = convertParams(params); const std::vector<thrift_codegen::MenuItem> menu_model = to_thrift(model); std::shared_ptr<RemoteCefRunContextMenuCallback> callback_wrapper = RemoteCefRunContextMenuCallback::wrapDelegate(callback); bool result = myService->exec<bool>( [&](const JavaService& s) -> bool { return s->ContextMenuHandler_RunContextMenu( bid, frm.serverId(), thriftParams, menu_model, callback_wrapper->serverId()); }, false); if (!result) { RemoteCefRunContextMenuCallback::dispose(callback_wrapper->getId()); } return result; } bool RemoteContextMenuHandler::OnContextMenuCommand( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefContextMenuParams> params, int command_id, EventFlags event_flags) { FIND_BID_OR_RETURN_VAL(false); RemoteFrame::Holder frm(frame); const auto thriftParams = convertParams(params); return myService->exec<bool>([&](const JavaService& s) { return s->ContextMenuHandler_OnContextMenuCommand( bid, frm.serverId(), thriftParams, command_id, event_flags); }, false); } void RemoteContextMenuHandler::OnContextMenuDismissed( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame) { FIND_BID_OR_RETURN(); RemoteFrame::Holder frm(frame); myService->exec([&](const JavaService& s) { s->ContextMenuHandler_OnContextMenuDismissed(bid, frm.serverId()); }); }