Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / renderer / pepper / pepper_flash_menu_host.cc
blob3b7a438f7220b6431f43bda5fe7c6123ed8ba0f8
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/pepper/pepper_flash_menu_host.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "content/public/common/context_menu_params.h"
9 #include "content/public/renderer/render_frame.h"
10 #include "content/public/renderer/renderer_ppapi_host.h"
11 #include "ipc/ipc_message.h"
12 #include "ppapi/c/private/ppb_flash_menu.h"
13 #include "ppapi/host/dispatch_host_message.h"
14 #include "ppapi/host/ppapi_host.h"
15 #include "ppapi/proxy/ppapi_messages.h"
16 #include "ppapi/proxy/serialized_flash_menu.h"
17 #include "ui/gfx/geometry/point.h"
19 namespace {
21 // Maximum depth of submenus allowed (e.g., 1 indicates that submenus are
22 // allowed, but not sub-submenus).
23 const size_t kMaxMenuDepth = 2;
25 // Maximum number of entries in any single menu (including separators).
26 const size_t kMaxMenuEntries = 50;
28 // Maximum total number of entries in the |menu_id_map| (see below).
29 // (Limit to 500 real entries; reserve the 0 action as an invalid entry.)
30 const size_t kMaxMenuIdMapEntries = 501;
32 // Converts menu data from one form to another.
33 // - |depth| is the current nested depth (call it starting with 0).
34 // - |menu_id_map| is such that |menu_id_map[output_item.action] ==
35 // input_item.id| (where |action| is what a |MenuItem| has, |id| is what a
36 // |PP_Flash_MenuItem| has).
37 bool ConvertMenuData(const PP_Flash_Menu* in_menu,
38 size_t depth,
39 std::vector<content::MenuItem>* out_menu,
40 std::vector<int32_t>* menu_id_map) {
41 if (depth > kMaxMenuDepth || !in_menu)
42 return false;
44 // Clear the output, just in case.
45 out_menu->clear();
47 if (!in_menu->count)
48 return true; // Nothing else to do.
50 if (!in_menu->items || in_menu->count > kMaxMenuEntries)
51 return false;
52 for (uint32_t i = 0; i < in_menu->count; i++) {
53 content::MenuItem item;
55 PP_Flash_MenuItem_Type type = in_menu->items[i].type;
56 switch (type) {
57 case PP_FLASH_MENUITEM_TYPE_NORMAL:
58 item.type = content::MenuItem::OPTION;
59 break;
60 case PP_FLASH_MENUITEM_TYPE_CHECKBOX:
61 item.type = content::MenuItem::CHECKABLE_OPTION;
62 break;
63 case PP_FLASH_MENUITEM_TYPE_SEPARATOR:
64 item.type = content::MenuItem::SEPARATOR;
65 break;
66 case PP_FLASH_MENUITEM_TYPE_SUBMENU:
67 item.type = content::MenuItem::SUBMENU;
68 break;
69 default:
70 return false;
72 if (in_menu->items[i].name)
73 item.label = base::UTF8ToUTF16(in_menu->items[i].name);
74 if (menu_id_map->size() >= kMaxMenuIdMapEntries)
75 return false;
76 item.action = static_cast<unsigned>(menu_id_map->size());
77 // This sets |(*menu_id_map)[item.action] = in_menu->items[i].id|.
78 menu_id_map->push_back(in_menu->items[i].id);
79 item.enabled = PP_ToBool(in_menu->items[i].enabled);
80 item.checked = PP_ToBool(in_menu->items[i].checked);
81 if (type == PP_FLASH_MENUITEM_TYPE_SUBMENU) {
82 if (!ConvertMenuData(
83 in_menu->items[i].submenu, depth + 1, &item.submenu, menu_id_map))
84 return false;
87 out_menu->push_back(item);
90 return true;
93 } // namespace
95 PepperFlashMenuHost::PepperFlashMenuHost(
96 content::RendererPpapiHost* host,
97 PP_Instance instance,
98 PP_Resource resource,
99 const ppapi::proxy::SerializedFlashMenu& serial_menu)
100 : ppapi::host::ResourceHost(host->GetPpapiHost(), instance, resource),
101 renderer_ppapi_host_(host),
102 showing_context_menu_(false),
103 context_menu_request_id_(0),
104 has_saved_context_menu_action_(false),
105 saved_context_menu_action_(0) {
106 menu_id_map_.push_back(0); // Reserve |menu_id_map_[0]|.
107 if (!ConvertMenuData(serial_menu.pp_menu(), 0, &menu_data_, &menu_id_map_)) {
108 menu_data_.clear();
109 menu_id_map_.clear();
113 PepperFlashMenuHost::~PepperFlashMenuHost() {
114 if (showing_context_menu_) {
115 content::RenderFrame* render_frame =
116 renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance());
117 if (render_frame)
118 render_frame->CancelContextMenu(context_menu_request_id_);
122 int32_t PepperFlashMenuHost::OnResourceMessageReceived(
123 const IPC::Message& msg,
124 ppapi::host::HostMessageContext* context) {
125 PPAPI_BEGIN_MESSAGE_MAP(PepperFlashMenuHost, msg)
126 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashMenu_Show,
127 OnHostMsgShow)
128 PPAPI_END_MESSAGE_MAP()
129 return PP_ERROR_FAILED;
132 int32_t PepperFlashMenuHost::OnHostMsgShow(
133 ppapi::host::HostMessageContext* context,
134 const PP_Point& location) {
135 // Note that all early returns must do a SendMenuReply. The sync result for
136 // this message isn't used, so to forward the error to the plugin, we need to
137 // additionally call SendMenuReply explicitly.
138 if (menu_data_.empty()) {
139 SendMenuReply(PP_ERROR_FAILED, -1);
140 return PP_ERROR_FAILED;
142 if (showing_context_menu_) {
143 SendMenuReply(PP_ERROR_INPROGRESS, -1);
144 return PP_ERROR_INPROGRESS;
147 content::RenderFrame* render_frame =
148 renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance());
150 content::ContextMenuParams params;
151 params.x = location.x;
152 params.y = location.y;
153 params.custom_context.is_pepper_menu = true;
154 params.custom_context.render_widget_id =
155 renderer_ppapi_host_->GetRoutingIDForWidget(pp_instance());
156 params.custom_items = menu_data_;
158 // Transform the position to be in render frame's coordinates.
159 gfx::Point render_frame_pt = renderer_ppapi_host_->PluginPointToRenderFrame(
160 pp_instance(), gfx::Point(location.x, location.y));
161 params.x = render_frame_pt.x();
162 params.y = render_frame_pt.y();
164 showing_context_menu_ = true;
165 context_menu_request_id_ = render_frame->ShowContextMenu(this, params);
167 // Note: the show message is sync so this OK is for the sync reply which we
168 // don't actually use (see the comment in the resource file for this). The
169 // async message containing the context menu action will be sent in the
170 // future.
171 return PP_OK;
174 void PepperFlashMenuHost::OnMenuAction(int request_id, unsigned action) {
175 // Just save the action.
176 DCHECK(!has_saved_context_menu_action_);
177 has_saved_context_menu_action_ = true;
178 saved_context_menu_action_ = action;
181 void PepperFlashMenuHost::OnMenuClosed(int request_id) {
182 if (has_saved_context_menu_action_ &&
183 saved_context_menu_action_ < menu_id_map_.size()) {
184 SendMenuReply(PP_OK, menu_id_map_[saved_context_menu_action_]);
185 has_saved_context_menu_action_ = false;
186 saved_context_menu_action_ = 0;
187 } else {
188 SendMenuReply(PP_ERROR_USERCANCEL, -1);
191 showing_context_menu_ = false;
192 context_menu_request_id_ = 0;
195 void PepperFlashMenuHost::SendMenuReply(int32_t result, int action) {
196 ppapi::host::ReplyMessageContext reply_context(
197 ppapi::proxy::ResourceMessageReplyParams(pp_resource(), 0),
198 NULL,
199 MSG_ROUTING_NONE);
200 reply_context.params.set_result(result);
201 host()->SendReply(reply_context, PpapiPluginMsg_FlashMenu_ShowReply(action));