| Index: webkit/plugins/ppapi/ppb_flash_menu_impl.cc
|
| diff --git a/webkit/plugins/ppapi/ppb_flash_menu_impl.cc b/webkit/plugins/ppapi/ppb_flash_menu_impl.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..67776feefbd71762acd113b3f9f3ceac041d562c
|
| --- /dev/null
|
| +++ b/webkit/plugins/ppapi/ppb_flash_menu_impl.cc
|
| @@ -0,0 +1,223 @@
|
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "webkit/plugins/ppapi/ppb_flash_menu_impl.h"
|
| +
|
| +#include "base/utf_string_conversions.h"
|
| +#include "gfx/point.h"
|
| +#include "ppapi/c/pp_completion_callback.h"
|
| +#include "webkit/glue/webmenuitem.h"
|
| +#include "webkit/plugins/ppapi/common.h"
|
| +#include "webkit/plugins/ppapi/plugin_delegate.h"
|
| +#include "webkit/plugins/ppapi/plugin_module.h"
|
| +#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
|
| +
|
| +namespace webkit {
|
| +namespace ppapi {
|
| +
|
| +namespace {
|
| +
|
| +PP_Resource Create(PP_Instance instance_id, const PP_Flash_Menu* menu_data) {
|
| + PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
|
| + if (!instance)
|
| + return 0;
|
| +
|
| + scoped_refptr<PPB_Flash_Menu_Impl> menu(new PPB_Flash_Menu_Impl(instance));
|
| + if (!menu->Init(menu_data))
|
| + return 0;
|
| +
|
| + return menu->GetReference();
|
| +}
|
| +
|
| +PP_Bool IsFlashMenu(PP_Resource resource) {
|
| + return BoolToPPBool(!!Resource::GetAs<PPB_Flash_Menu_Impl>(resource));
|
| +}
|
| +
|
| +int32_t Show(PP_Resource menu_id,
|
| + const PP_Point* location,
|
| + int32_t* selected_id,
|
| + PP_CompletionCallback callback) {
|
| + scoped_refptr<PPB_Flash_Menu_Impl> menu(
|
| + Resource::GetAs<PPB_Flash_Menu_Impl>(menu_id));
|
| + if (!menu.get())
|
| + return PP_ERROR_BADRESOURCE;
|
| +
|
| + return menu->Show(location, selected_id, callback);
|
| +}
|
| +
|
| +const PPB_Flash_Menu ppb_flash_menu = {
|
| + &Create,
|
| + &IsFlashMenu,
|
| + &Show,
|
| +};
|
| +
|
| +// Maximum depth of submenus allowed (e.g., 1 indicates that submenus are
|
| +// allowed, but not sub-submenus).
|
| +const size_t kMaxMenuDepth = 2;
|
| +
|
| +// Maximum number of entries in any single menu (including separators).
|
| +const size_t kMaxMenuEntries = 50;
|
| +
|
| +// Maximum total number of entries in the |menu_id_map| (see below).
|
| +// (Limit to 500 real entries; reserve the 0 action as an invalid entry.)
|
| +const size_t kMaxMenuIdMapEntries = 501;
|
| +
|
| +// Converts menu data from one form to another.
|
| +// - |depth| is the current nested depth (call it starting with 0).
|
| +// - |menu_id_map| is such that |menu_id_map[output_item.action] ==
|
| +// input_item.id| (where |action| is what a |WebMenuItem| has, |id| is what a
|
| +// |PP_Flash_MenuItem| has).
|
| +bool ConvertMenuData(const PP_Flash_Menu* in_menu,
|
| + size_t depth,
|
| + PPB_Flash_Menu_Impl::MenuData* out_menu,
|
| + std::vector<int32_t>* menu_id_map) {
|
| + if (depth > kMaxMenuDepth)
|
| + return false;
|
| +
|
| + // Clear the output, just in case.
|
| + out_menu->clear();
|
| +
|
| + if (!in_menu || !in_menu->count)
|
| + return true; // Nothing else to do.
|
| +
|
| + if (!in_menu->items || in_menu->count > kMaxMenuEntries)
|
| + return false;
|
| + for (uint32_t i = 0; i < in_menu->count; i++) {
|
| + WebMenuItem item;
|
| +
|
| + PP_Flash_MenuItem_Type type = in_menu->items[i].type;
|
| + switch (type) {
|
| + case PP_FLASH_MENUITEM_TYPE_NORMAL:
|
| + item.type = WebMenuItem::OPTION;
|
| + break;
|
| + case PP_FLASH_MENUITEM_TYPE_CHECKBOX:
|
| + item.type = WebMenuItem::CHECKABLE_OPTION;
|
| + break;
|
| + case PP_FLASH_MENUITEM_TYPE_SEPARATOR:
|
| + item.type = WebMenuItem::SEPARATOR;
|
| + break;
|
| + case PP_FLASH_MENUITEM_TYPE_SUBMENU:
|
| + item.type = WebMenuItem::SUBMENU;
|
| + break;
|
| + default:
|
| + return false;
|
| + }
|
| + if (in_menu->items[i].name)
|
| + item.label = UTF8ToUTF16(in_menu->items[i].name);
|
| + if (menu_id_map->size() >= kMaxMenuIdMapEntries)
|
| + return false;
|
| + item.action = static_cast<unsigned>(menu_id_map->size());
|
| + // This sets |(*menu_id_map)[item.action] = in_menu->items[i].id|.
|
| + menu_id_map->push_back(in_menu->items[i].id);
|
| + item.enabled = PPBoolToBool(in_menu->items[i].enabled);
|
| + item.checked = PPBoolToBool(in_menu->items[i].checked);
|
| + if (type == PP_FLASH_MENUITEM_TYPE_SUBMENU) {
|
| + if (!ConvertMenuData(in_menu->items[i].submenu, depth + 1, &item.submenu,
|
| + menu_id_map))
|
| + return false;
|
| + }
|
| +
|
| + out_menu->push_back(item);
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +PPB_Flash_Menu_Impl::PPB_Flash_Menu_Impl(PluginInstance* instance)
|
| + : Resource(instance) {
|
| +}
|
| +
|
| +bool PPB_Flash_Menu_Impl::Init(const PP_Flash_Menu* menu_data) {
|
| + menu_id_map_.clear();
|
| + menu_id_map_.push_back(0); // Reserve |menu_id_map_[0]|.
|
| + if (!ConvertMenuData(menu_data, 0, &menu_data_, &menu_id_map_)) {
|
| + menu_id_map_.clear();
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +PPB_Flash_Menu_Impl::~PPB_Flash_Menu_Impl() {
|
| +}
|
| +
|
| +// static
|
| +const PPB_Flash_Menu* PPB_Flash_Menu_Impl::GetInterface() {
|
| + return &ppb_flash_menu;
|
| +}
|
| +
|
| +PPB_Flash_Menu_Impl* PPB_Flash_Menu_Impl::AsPPB_Flash_Menu_Impl() {
|
| + return this;
|
| +}
|
| +
|
| +int32_t PPB_Flash_Menu_Impl::Show(const PP_Point* location,
|
| + int32_t* selected_id_out,
|
| + PP_CompletionCallback callback) {
|
| + // |location| is not (currently) optional.
|
| + // TODO(viettrungluu): Make it optional and default to the current mouse pos?
|
| + if (!location)
|
| + return PP_ERROR_BADARGUMENT;
|
| +
|
| + if (!callback.func) {
|
| + NOTIMPLEMENTED();
|
| + return PP_ERROR_BADARGUMENT;
|
| + }
|
| +
|
| + if (callback_.get() && !callback_->completed())
|
| + return PP_ERROR_INPROGRESS;
|
| +
|
| + PP_Resource resource_id = GetReferenceNoAddRef();
|
| + if (!resource_id) {
|
| + NOTREACHED();
|
| + return PP_ERROR_FAILED;
|
| + }
|
| +
|
| + int32_t rv = instance()->delegate()->ShowContextMenu(
|
| + this, gfx::Point(instance()->position().x() + location->x,
|
| + instance()->position().y() + location->y));
|
| + if (rv == PP_ERROR_WOULDBLOCK) {
|
| + // Record callback and output buffers.
|
| + callback_ = new TrackedCompletionCallback(
|
| + instance()->module()->GetCallbackTracker(), resource_id, callback);
|
| + selected_id_out_ = selected_id_out;
|
| + } else {
|
| + // This should never be completed synchronously successfully.
|
| + DCHECK_NE(rv, PP_OK);
|
| + }
|
| + return rv;
|
| +
|
| + NOTIMPLEMENTED();
|
| + return PP_ERROR_FAILED;
|
| +}
|
| +
|
| +void PPB_Flash_Menu_Impl::CompleteShow(int32_t result,
|
| + unsigned action) {
|
| + int32_t rv = PP_ERROR_ABORTED;
|
| + if (!callback_->aborted()) {
|
| + CHECK(!callback_->completed());
|
| + rv = result;
|
| +
|
| + // Write output data.
|
| + if (selected_id_out_ && result == PP_OK) {
|
| + // We reserved action 0 to be invalid.
|
| + if (action == 0 || action >= menu_id_map_.size()) {
|
| + NOTREACHED() << "Invalid action received.";
|
| + rv = PP_ERROR_FAILED;
|
| + } else {
|
| + *selected_id_out_ = menu_id_map_[action];
|
| + }
|
| + }
|
| + }
|
| +
|
| + scoped_refptr<TrackedCompletionCallback> callback;
|
| + callback.swap(callback_);
|
| + selected_id_out_ = NULL;
|
| +
|
| + callback->Run(rv); // Will complete abortively if necessary.
|
| +}
|
| +
|
| +} // namespace ppapi
|
| +} // namespace webkit
|
|
|