Chromium Code Reviews| Index: Source/core/page/CustomContextMenuProvider.cpp |
| diff --git a/Source/core/page/CustomContextMenuProvider.cpp b/Source/core/page/CustomContextMenuProvider.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6e69b9233c3f5a651e6d9227070ac79f06d924c6 |
| --- /dev/null |
| +++ b/Source/core/page/CustomContextMenuProvider.cpp |
| @@ -0,0 +1,119 @@ |
| +// Copyright 2014 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 "config.h" |
| +#include "core/page/CustomContextMenuProvider.h" |
| + |
| +#include "core/dom/Document.h" |
| +#include "core/dom/ElementTraversal.h" |
| +#include "core/events/EventDispatcher.h" |
| +#include "core/events/MouseEvent.h" |
| +#include "core/html/HTMLMenuElement.h" |
| +#include "core/html/HTMLMenuItemElement.h" |
| +#include "core/page/ContextMenuController.h" |
| +#include "core/page/Page.h" |
| +#include "platform/ContextMenu.h" |
| + |
| +namespace blink { |
| + |
| +using namespace HTMLNames; |
| + |
| +CustomContextMenuProvider::CustomContextMenuProvider(HTMLMenuElement& menu, HTMLElement& subject) |
| + : m_menu(menu) |
| + , m_subjectElement(subject) |
| +{ |
| +} |
| + |
| +CustomContextMenuProvider::~CustomContextMenuProvider() |
| +{ |
| +} |
| + |
| +void CustomContextMenuProvider::populateContextMenu(ContextMenu* menu) |
| +{ |
| + populateContextMenuItems(*m_menu, *menu); |
| +} |
| + |
| +void CustomContextMenuProvider::contextMenuItemSelected(const ContextMenuItem* item) |
| +{ |
| + if (HTMLElement* element = menuItemAt(item->action())) { |
| + RefPtrWillBeRawPtr<SimulatedMouseEvent> click = SimulatedMouseEvent::create(EventTypeNames::click, m_menu->document().domWindow(), Event::create()); |
| + click->setRelatedTarget(m_subjectElement.get()); |
| + element->dispatchEvent(click.release()); |
| + } |
| +} |
| + |
| +void CustomContextMenuProvider::contextMenuCleared() |
| +{ |
| + m_menuItems.clear(); |
| + m_subjectElement = nullptr; |
| +} |
| + |
| +void CustomContextMenuProvider::appendSeparator(ContextMenu& contextMenu) |
| +{ |
| + // Avoid separators at the start of any menu and submenu. |
| + if (!contextMenu.items().size()) |
| + return; |
| + |
| + // Collapse all sequences of two or more adjacent separators in the menu or any submenus to a single separator. |
|
tkent
2014/08/25 23:07:42
I recommend to wrap code comments in 80 columns.
pals
2014/08/26 07:13:23
Done.
|
| + ContextMenuItem lastItem = contextMenu.items().last(); |
| + if (lastItem.type() == SeparatorType) |
| + return; |
| + |
| + contextMenu.appendItem(ContextMenuItem(SeparatorType, ContextMenuItemCustomTagNoAction, String())); |
| +} |
| + |
| +void CustomContextMenuProvider::appendMenuItem(HTMLMenuItemElement* menuItem, ContextMenu& contextMenu) |
| +{ |
| + // Avoid menuitems with no label. |
| + String labelString = menuItem->fastGetAttribute(labelAttr); |
| + if (labelString.isNull() || labelString.isEmpty()) |
|
tkent
2014/08/25 23:07:42
isEmpty() is enough. isEmpty() contains isNull().
pals
2014/08/26 07:13:23
Done.
|
| + return; |
| + |
| + m_menuItems.append(menuItem); |
| + contextMenu.appendItem(ContextMenuItem(ActionType, static_cast<ContextMenuAction>(ContextMenuItemBaseCustomTag + m_menuItems.size() - 1), labelString)); |
| +} |
| + |
| +void CustomContextMenuProvider::populateContextMenuItems(const HTMLMenuElement& menu, ContextMenu& contextMenu) |
| +{ |
| + HTMLElement* nextElement = Traversal<HTMLElement>::firstWithin(menu); |
| + while (nextElement) { |
| + if (isHTMLHRElement(*nextElement)) { |
| + appendSeparator(contextMenu); |
| + nextElement = Traversal<HTMLElement>::next(*nextElement, &menu); |
| + } else if (isHTMLMenuElement(*nextElement)) { |
| + ContextMenu subMenu; |
| + String labelString = nextElement->fastGetAttribute(labelAttr); |
| + if (labelString.isNull() || labelString.isEmpty()) { |
|
tkent
2014/08/25 23:07:42
This looks incorrect.
The following code is for t
pals
2014/08/26 07:13:23
My understanding from the above two statements is
tkent
2014/08/26 08:11:26
ok, let's revisit here after the clarification.
|
| + appendSeparator(contextMenu); |
| + populateContextMenuItems(*toHTMLMenuElement(nextElement), contextMenu); |
| + appendSeparator(contextMenu); |
| + } else { |
| + populateContextMenuItems(*toHTMLMenuElement(nextElement), subMenu); |
| + contextMenu.appendItem(ContextMenuItem(SubmenuType, ContextMenuItemCustomTagNoAction, labelString, &subMenu)); |
| + } |
| + nextElement = Traversal<HTMLElement>::nextSibling(*nextElement); |
| + } else if (isHTMLMenuItemElement(*nextElement)) { |
| + appendMenuItem(toHTMLMenuItemElement(nextElement), contextMenu); |
| + if (ContextMenuItemBaseCustomTag + m_menuItems.size() >= ContextMenuItemLastCustomTag) |
| + break; |
| + nextElement = Traversal<HTMLElement>::next(*nextElement, &menu); |
| + } else { |
| + nextElement = Traversal<HTMLElement>::next(*nextElement, &menu); |
| + } |
| + } |
| + |
| + // Remove separators at the end of the menu and any submenus. |
| + while (contextMenu.items().last().type() == SeparatorType) |
|
tkent
2014/08/25 23:07:42
Need a test in a case of <menu type=popup> without
pals
2014/08/26 07:13:23
Done.
|
| + contextMenu.removeLastItem(); |
| +} |
| + |
| +HTMLElement* CustomContextMenuProvider::menuItemAt(unsigned menuId) |
| +{ |
| + int itemIndex = menuId - ContextMenuItemBaseCustomTag; |
| + if (itemIndex < 0 || static_cast<unsigned long>(itemIndex) >= m_menuItems.size()) |
| + return 0; |
| + return m_menuItems[itemIndex].get(); |
| +} |
| + |
| +} // namespace blink |