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"
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
,
39 std::vector
<content::MenuItem
>* out_menu
,
40 std::vector
<int32_t>* menu_id_map
) {
41 if (depth
> kMaxMenuDepth
|| !in_menu
)
44 // Clear the output, just in case.
48 return true; // Nothing else to do.
50 if (!in_menu
->items
|| in_menu
->count
> kMaxMenuEntries
)
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
;
57 case PP_FLASH_MENUITEM_TYPE_NORMAL
:
58 item
.type
= content::MenuItem::OPTION
;
60 case PP_FLASH_MENUITEM_TYPE_CHECKBOX
:
61 item
.type
= content::MenuItem::CHECKABLE_OPTION
;
63 case PP_FLASH_MENUITEM_TYPE_SEPARATOR
:
64 item
.type
= content::MenuItem::SEPARATOR
;
66 case PP_FLASH_MENUITEM_TYPE_SUBMENU
:
67 item
.type
= content::MenuItem::SUBMENU
;
72 if (in_menu
->items
[i
].name
)
73 item
.label
= base::UTF8ToUTF16(in_menu
->items
[i
].name
);
74 if (menu_id_map
->size() >= kMaxMenuIdMapEntries
)
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
) {
83 in_menu
->items
[i
].submenu
, depth
+ 1, &item
.submenu
, menu_id_map
))
87 out_menu
->push_back(item
);
95 PepperFlashMenuHost::PepperFlashMenuHost(
96 content::RendererPpapiHost
* host
,
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_
)) {
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());
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
,
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
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;
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),
200 reply_context
.params
.set_result(result
);
201 host()->SendReply(reply_context
, PpapiPluginMsg_FlashMenu_ShowReply(action
));