Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / renderer / pepper / pepper_flash_menu_host.cc
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.
4
5 #include "chrome/renderer/pepper/pepper_flash_menu_host.h"
6
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/point.h"
18
19 namespace {
20
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;
24
25 // Maximum number of entries in any single menu (including separators).
26 const size_t kMaxMenuEntries = 50;
27
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;
31
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;
43
44   // Clear the output, just in case.
45   out_menu->clear();
46
47   if (!in_menu->count)
48     return true;  // Nothing else to do.
49
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;
54
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;
71     }
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;
85     }
86
87     out_menu->push_back(item);
88   }
89
90   return true;
91 }
92
93 }  // namespace
94
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();
110   }
111 }
112
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_);
119   }
120 }
121
122 int32_t PepperFlashMenuHost::OnResourceMessageReceived(
123     const IPC::Message& msg,
124     ppapi::host::HostMessageContext* context) {
125   IPC_BEGIN_MESSAGE_MAP(PepperFlashMenuHost, msg)
126   PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashMenu_Show, OnHostMsgShow)
127   IPC_END_MESSAGE_MAP()
128   return PP_ERROR_FAILED;
129 }
130
131 int32_t PepperFlashMenuHost::OnHostMsgShow(
132     ppapi::host::HostMessageContext* context,
133     const PP_Point& location) {
134   // Note that all early returns must do a SendMenuReply. The sync result for
135   // this message isn't used, so to forward the error to the plugin, we need to
136   // additionally call SendMenuReply explicitly.
137   if (menu_data_.empty()) {
138     SendMenuReply(PP_ERROR_FAILED, -1);
139     return PP_ERROR_FAILED;
140   }
141   if (showing_context_menu_) {
142     SendMenuReply(PP_ERROR_INPROGRESS, -1);
143     return PP_ERROR_INPROGRESS;
144   }
145
146   content::RenderFrame* render_frame =
147       renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance());
148
149   content::ContextMenuParams params;
150   params.x = location.x;
151   params.y = location.y;
152   params.custom_context.is_pepper_menu = true;
153   params.custom_context.render_widget_id =
154       renderer_ppapi_host_->GetRoutingIDForWidget(pp_instance());
155   params.custom_items = menu_data_;
156
157   // Transform the position to be in render frame's coordinates.
158   gfx::Point render_frame_pt = renderer_ppapi_host_->PluginPointToRenderFrame(
159       pp_instance(), gfx::Point(location.x, location.y));
160   params.x = render_frame_pt.x();
161   params.y = render_frame_pt.y();
162
163   showing_context_menu_ = true;
164   context_menu_request_id_ = render_frame->ShowContextMenu(this, params);
165
166   // Note: the show message is sync so this OK is for the sync reply which we
167   // don't actually use (see the comment in the resource file for this). The
168   // async message containing the context menu action will be sent in the
169   // future.
170   return PP_OK;
171 }
172
173 void PepperFlashMenuHost::OnMenuAction(int request_id, unsigned action) {
174   // Just save the action.
175   DCHECK(!has_saved_context_menu_action_);
176   has_saved_context_menu_action_ = true;
177   saved_context_menu_action_ = action;
178 }
179
180 void PepperFlashMenuHost::OnMenuClosed(int request_id) {
181   if (has_saved_context_menu_action_ &&
182       saved_context_menu_action_ < menu_id_map_.size()) {
183     SendMenuReply(PP_OK, menu_id_map_[saved_context_menu_action_]);
184     has_saved_context_menu_action_ = false;
185     saved_context_menu_action_ = 0;
186   } else {
187     SendMenuReply(PP_ERROR_USERCANCEL, -1);
188   }
189
190   showing_context_menu_ = false;
191   context_menu_request_id_ = 0;
192 }
193
194 void PepperFlashMenuHost::SendMenuReply(int32_t result, int action) {
195   ppapi::host::ReplyMessageContext reply_context(
196       ppapi::proxy::ResourceMessageReplyParams(pp_resource(), 0),
197       NULL,
198       MSG_ROUTING_NONE);
199   reply_context.params.set_result(result);
200   host()->SendReply(reply_context, PpapiPluginMsg_FlashMenu_ShowReply(action));
201 }