1// Copyright 2014 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 "config.h" 6#include "core/page/CustomContextMenuProvider.h" 7 8#include "core/dom/Document.h" 9#include "core/dom/ElementTraversal.h" 10#include "core/events/EventDispatcher.h" 11#include "core/events/MouseEvent.h" 12#include "core/html/HTMLMenuElement.h" 13#include "core/html/HTMLMenuItemElement.h" 14#include "core/page/ContextMenuController.h" 15#include "core/page/Page.h" 16#include "platform/ContextMenu.h" 17 18namespace blink { 19 20using namespace HTMLNames; 21 22CustomContextMenuProvider::CustomContextMenuProvider(HTMLMenuElement& menu, HTMLElement& subject) 23 : m_menu(menu) 24 , m_subjectElement(subject) 25{ 26} 27 28CustomContextMenuProvider::~CustomContextMenuProvider() 29{ 30} 31 32void CustomContextMenuProvider::populateContextMenu(ContextMenu* menu) 33{ 34 populateContextMenuItems(*m_menu, *menu); 35} 36 37void CustomContextMenuProvider::contextMenuItemSelected(const ContextMenuItem* item) 38{ 39 if (HTMLElement* element = menuItemAt(item->action())) { 40 RefPtrWillBeRawPtr<SimulatedMouseEvent> click = SimulatedMouseEvent::create(EventTypeNames::click, m_menu->document().domWindow(), Event::create()); 41 click->setRelatedTarget(m_subjectElement.get()); 42 element->dispatchEvent(click.release()); 43 } 44} 45 46void CustomContextMenuProvider::contextMenuCleared() 47{ 48 m_menuItems.clear(); 49 m_subjectElement = nullptr; 50} 51 52void CustomContextMenuProvider::appendSeparator(ContextMenu& contextMenu) 53{ 54 // Avoid separators at the start of any menu and submenu. 55 if (!contextMenu.items().size()) 56 return; 57 58 // Collapse all sequences of two or more adjacent separators in the menu or 59 // any submenus to a single separator. 60 ContextMenuItem lastItem = contextMenu.items().last(); 61 if (lastItem.type() == SeparatorType) 62 return; 63 64 contextMenu.appendItem(ContextMenuItem(SeparatorType, ContextMenuItemCustomTagNoAction, String())); 65} 66 67void CustomContextMenuProvider::appendMenuItem(HTMLMenuItemElement* menuItem, ContextMenu& contextMenu) 68{ 69 // Avoid menuitems with no label. 70 String labelString = menuItem->fastGetAttribute(labelAttr); 71 if (labelString.isEmpty()) 72 return; 73 74 m_menuItems.append(menuItem); 75 contextMenu.appendItem(ContextMenuItem(ActionType, static_cast<ContextMenuAction>(ContextMenuItemBaseCustomTag + m_menuItems.size() - 1), labelString)); 76} 77 78void CustomContextMenuProvider::populateContextMenuItems(const HTMLMenuElement& menu, ContextMenu& contextMenu) 79{ 80 HTMLElement* nextElement = Traversal<HTMLElement>::firstWithin(menu); 81 while (nextElement) { 82 if (isHTMLHRElement(*nextElement)) { 83 appendSeparator(contextMenu); 84 nextElement = Traversal<HTMLElement>::next(*nextElement, &menu); 85 } else if (isHTMLMenuElement(*nextElement)) { 86 ContextMenu subMenu; 87 String labelString = nextElement->fastGetAttribute(labelAttr); 88 if (labelString.isNull()) { 89 appendSeparator(contextMenu); 90 populateContextMenuItems(*toHTMLMenuElement(nextElement), contextMenu); 91 appendSeparator(contextMenu); 92 } else if (!labelString.isEmpty()) { 93 populateContextMenuItems(*toHTMLMenuElement(nextElement), subMenu); 94 contextMenu.appendItem(ContextMenuItem(SubmenuType, ContextMenuItemCustomTagNoAction, labelString, &subMenu)); 95 } 96 nextElement = Traversal<HTMLElement>::nextSibling(*nextElement); 97 } else if (isHTMLMenuItemElement(*nextElement)) { 98 appendMenuItem(toHTMLMenuItemElement(nextElement), contextMenu); 99 if (ContextMenuItemBaseCustomTag + m_menuItems.size() >= ContextMenuItemLastCustomTag) 100 break; 101 nextElement = Traversal<HTMLElement>::next(*nextElement, &menu); 102 } else { 103 nextElement = Traversal<HTMLElement>::next(*nextElement, &menu); 104 } 105 } 106 107 // Remove separators at the end of the menu and any submenus. 108 while (contextMenu.items().size() && contextMenu.items().last().type() == SeparatorType) 109 contextMenu.removeLastItem(); 110} 111 112HTMLElement* CustomContextMenuProvider::menuItemAt(unsigned menuId) 113{ 114 int itemIndex = menuId - ContextMenuItemBaseCustomTag; 115 if (itemIndex < 0 || static_cast<unsigned long>(itemIndex) >= m_menuItems.size()) 116 return 0; 117 return m_menuItems[itemIndex].get(); 118} 119 120} // namespace blink 121