OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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 "webkit/plugins/ppapi/ppb_flash_menu_impl.h" |
| 6 |
| 7 #include "base/utf_string_conversions.h" |
| 8 #include "gfx/point.h" |
| 9 #include "ppapi/c/pp_completion_callback.h" |
| 10 #include "webkit/glue/webmenuitem.h" |
| 11 #include "webkit/plugins/ppapi/common.h" |
| 12 #include "webkit/plugins/ppapi/plugin_delegate.h" |
| 13 #include "webkit/plugins/ppapi/plugin_module.h" |
| 14 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" |
| 15 |
| 16 namespace webkit { |
| 17 namespace ppapi { |
| 18 |
| 19 namespace { |
| 20 |
| 21 PP_Resource Create(PP_Instance instance_id, const PP_Flash_Menu* menu_data) { |
| 22 PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id); |
| 23 if (!instance) |
| 24 return 0; |
| 25 |
| 26 scoped_refptr<PPB_Flash_Menu_Impl> menu(new PPB_Flash_Menu_Impl(instance)); |
| 27 if (!menu->Init(menu_data)) |
| 28 return 0; |
| 29 |
| 30 return menu->GetReference(); |
| 31 } |
| 32 |
| 33 PP_Bool IsFlashMenu(PP_Resource resource) { |
| 34 return BoolToPPBool(!!Resource::GetAs<PPB_Flash_Menu_Impl>(resource)); |
| 35 } |
| 36 |
| 37 int32_t Show(PP_Resource menu_id, |
| 38 const PP_Point* location, |
| 39 int32_t* selected_id, |
| 40 PP_CompletionCallback callback) { |
| 41 scoped_refptr<PPB_Flash_Menu_Impl> menu( |
| 42 Resource::GetAs<PPB_Flash_Menu_Impl>(menu_id)); |
| 43 if (!menu.get()) |
| 44 return PP_ERROR_BADRESOURCE; |
| 45 |
| 46 return menu->Show(location, selected_id, callback); |
| 47 } |
| 48 |
| 49 const PPB_Flash_Menu ppb_flash_menu = { |
| 50 &Create, |
| 51 &IsFlashMenu, |
| 52 &Show, |
| 53 }; |
| 54 |
| 55 // Maximum depth of submenus allowed (e.g., 1 indicates that submenus are |
| 56 // allowed, but not sub-submenus). |
| 57 const size_t kMaxMenuDepth = 2; |
| 58 |
| 59 // Maximum number of entries in any single menu (including separators). |
| 60 const size_t kMaxMenuEntries = 50; |
| 61 |
| 62 // Maximum total number of entries in the |menu_id_map| (see below). |
| 63 // (Limit to 500 real entries; reserve the 0 action as an invalid entry.) |
| 64 const size_t kMaxMenuIdMapEntries = 501; |
| 65 |
| 66 // Converts menu data from one form to another. |
| 67 // - |depth| is the current nested depth (call it starting with 0). |
| 68 // - |menu_id_map| is such that |menu_id_map[output_item.action] == |
| 69 // input_item.id| (where |action| is what a |WebMenuItem| has, |id| is what a |
| 70 // |PP_Flash_MenuItem| has). |
| 71 bool ConvertMenuData(const PP_Flash_Menu* in_menu, |
| 72 size_t depth, |
| 73 PPB_Flash_Menu_Impl::MenuData* out_menu, |
| 74 std::vector<int32_t>* menu_id_map) { |
| 75 if (depth > kMaxMenuDepth) |
| 76 return false; |
| 77 |
| 78 // Clear the output, just in case. |
| 79 out_menu->clear(); |
| 80 |
| 81 if (!in_menu || !in_menu->count) |
| 82 return true; // Nothing else to do. |
| 83 |
| 84 if (!in_menu->items || in_menu->count > kMaxMenuEntries) |
| 85 return false; |
| 86 for (uint32_t i = 0; i < in_menu->count; i++) { |
| 87 WebMenuItem item; |
| 88 |
| 89 PP_Flash_MenuItem_Type type = in_menu->items[i].type; |
| 90 switch (type) { |
| 91 case PP_FLASH_MENUITEM_TYPE_NORMAL: |
| 92 item.type = WebMenuItem::OPTION; |
| 93 break; |
| 94 case PP_FLASH_MENUITEM_TYPE_CHECKBOX: |
| 95 item.type = WebMenuItem::CHECKABLE_OPTION; |
| 96 break; |
| 97 case PP_FLASH_MENUITEM_TYPE_SEPARATOR: |
| 98 item.type = WebMenuItem::SEPARATOR; |
| 99 break; |
| 100 case PP_FLASH_MENUITEM_TYPE_SUBMENU: |
| 101 item.type = WebMenuItem::SUBMENU; |
| 102 break; |
| 103 default: |
| 104 return false; |
| 105 } |
| 106 if (in_menu->items[i].name) |
| 107 item.label = UTF8ToUTF16(in_menu->items[i].name); |
| 108 if (menu_id_map->size() >= kMaxMenuIdMapEntries) |
| 109 return false; |
| 110 item.action = static_cast<unsigned>(menu_id_map->size()); |
| 111 // This sets |(*menu_id_map)[item.action] = in_menu->items[i].id|. |
| 112 menu_id_map->push_back(in_menu->items[i].id); |
| 113 item.enabled = PPBoolToBool(in_menu->items[i].enabled); |
| 114 item.checked = PPBoolToBool(in_menu->items[i].checked); |
| 115 if (type == PP_FLASH_MENUITEM_TYPE_SUBMENU) { |
| 116 if (!ConvertMenuData(in_menu->items[i].submenu, depth + 1, &item.submenu, |
| 117 menu_id_map)) |
| 118 return false; |
| 119 } |
| 120 |
| 121 out_menu->push_back(item); |
| 122 } |
| 123 |
| 124 return true; |
| 125 } |
| 126 |
| 127 } // namespace |
| 128 |
| 129 PPB_Flash_Menu_Impl::PPB_Flash_Menu_Impl(PluginInstance* instance) |
| 130 : Resource(instance) { |
| 131 } |
| 132 |
| 133 bool PPB_Flash_Menu_Impl::Init(const PP_Flash_Menu* menu_data) { |
| 134 menu_id_map_.clear(); |
| 135 menu_id_map_.push_back(0); // Reserve |menu_id_map_[0]|. |
| 136 if (!ConvertMenuData(menu_data, 0, &menu_data_, &menu_id_map_)) { |
| 137 menu_id_map_.clear(); |
| 138 return false; |
| 139 } |
| 140 |
| 141 return true; |
| 142 } |
| 143 |
| 144 PPB_Flash_Menu_Impl::~PPB_Flash_Menu_Impl() { |
| 145 } |
| 146 |
| 147 // static |
| 148 const PPB_Flash_Menu* PPB_Flash_Menu_Impl::GetInterface() { |
| 149 return &ppb_flash_menu; |
| 150 } |
| 151 |
| 152 PPB_Flash_Menu_Impl* PPB_Flash_Menu_Impl::AsPPB_Flash_Menu_Impl() { |
| 153 return this; |
| 154 } |
| 155 |
| 156 int32_t PPB_Flash_Menu_Impl::Show(const PP_Point* location, |
| 157 int32_t* selected_id_out, |
| 158 PP_CompletionCallback callback) { |
| 159 // |location| is not (currently) optional. |
| 160 // TODO(viettrungluu): Make it optional and default to the current mouse pos? |
| 161 if (!location) |
| 162 return PP_ERROR_BADARGUMENT; |
| 163 |
| 164 if (!callback.func) { |
| 165 NOTIMPLEMENTED(); |
| 166 return PP_ERROR_BADARGUMENT; |
| 167 } |
| 168 |
| 169 if (callback_.get() && !callback_->completed()) |
| 170 return PP_ERROR_INPROGRESS; |
| 171 |
| 172 PP_Resource resource_id = GetReferenceNoAddRef(); |
| 173 if (!resource_id) { |
| 174 NOTREACHED(); |
| 175 return PP_ERROR_FAILED; |
| 176 } |
| 177 |
| 178 int32_t rv = instance()->delegate()->ShowContextMenu( |
| 179 this, gfx::Point(instance()->position().x() + location->x, |
| 180 instance()->position().y() + location->y)); |
| 181 if (rv == PP_ERROR_WOULDBLOCK) { |
| 182 // Record callback and output buffers. |
| 183 callback_ = new TrackedCompletionCallback( |
| 184 instance()->module()->GetCallbackTracker(), resource_id, callback); |
| 185 selected_id_out_ = selected_id_out; |
| 186 } else { |
| 187 // This should never be completed synchronously successfully. |
| 188 DCHECK_NE(rv, PP_OK); |
| 189 } |
| 190 return rv; |
| 191 |
| 192 NOTIMPLEMENTED(); |
| 193 return PP_ERROR_FAILED; |
| 194 } |
| 195 |
| 196 void PPB_Flash_Menu_Impl::CompleteShow(int32_t result, |
| 197 unsigned action) { |
| 198 int32_t rv = PP_ERROR_ABORTED; |
| 199 if (!callback_->aborted()) { |
| 200 CHECK(!callback_->completed()); |
| 201 rv = result; |
| 202 |
| 203 // Write output data. |
| 204 if (selected_id_out_ && result == PP_OK) { |
| 205 // We reserved action 0 to be invalid. |
| 206 if (action == 0 || action >= menu_id_map_.size()) { |
| 207 NOTREACHED() << "Invalid action received."; |
| 208 rv = PP_ERROR_FAILED; |
| 209 } else { |
| 210 *selected_id_out_ = menu_id_map_[action]; |
| 211 } |
| 212 } |
| 213 } |
| 214 |
| 215 scoped_refptr<TrackedCompletionCallback> callback; |
| 216 callback.swap(callback_); |
| 217 selected_id_out_ = NULL; |
| 218 |
| 219 callback->Run(rv); // Will complete abortively if necessary. |
| 220 } |
| 221 |
| 222 } // namespace ppapi |
| 223 } // namespace webkit |
OLD | NEW |