1/* 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 Igalia S.L 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "core/page/ContextMenuController.h" 29 30#include "core/dom/Document.h" 31#include "core/dom/Node.h" 32#include "core/events/Event.h" 33#include "core/events/MouseEvent.h" 34#include "core/events/RelatedEvent.h" 35#include "core/frame/LocalFrame.h" 36#include "core/html/HTMLMenuElement.h" 37#include "core/page/ContextMenuClient.h" 38#include "core/page/ContextMenuProvider.h" 39#include "core/page/CustomContextMenuProvider.h" 40#include "core/page/EventHandler.h" 41#include "platform/ContextMenu.h" 42#include "platform/ContextMenuItem.h" 43 44namespace blink { 45 46using namespace HTMLNames; 47 48ContextMenuController::ContextMenuController(Page*, ContextMenuClient* client) 49 : m_client(client) 50{ 51 ASSERT_ARG(client, client); 52} 53 54ContextMenuController::~ContextMenuController() 55{ 56} 57 58PassOwnPtrWillBeRawPtr<ContextMenuController> ContextMenuController::create(Page* page, ContextMenuClient* client) 59{ 60 return adoptPtrWillBeNoop(new ContextMenuController(page, client)); 61} 62 63void ContextMenuController::trace(Visitor* visitor) 64{ 65 visitor->trace(m_hitTestResult); 66} 67 68void ContextMenuController::clearContextMenu() 69{ 70 m_contextMenu.clear(); 71 if (m_menuProvider) 72 m_menuProvider->contextMenuCleared(); 73 m_menuProvider = nullptr; 74 m_client->clearContextMenu(); 75 m_hitTestResult = HitTestResult(); 76} 77 78void ContextMenuController::documentDetached(Document* document) 79{ 80 if (Node* innerNode = m_hitTestResult.innerNode()) { 81 // Invalidate the context menu info if its target document is detached. 82 if (innerNode->document() == document) 83 clearContextMenu(); 84 } 85} 86 87void ContextMenuController::populateCustomContextMenu(const Event& event) 88{ 89 if (!RuntimeEnabledFeatures::contextMenuEnabled()) 90 return; 91 92 Node* node = event.target()->toNode(); 93 if (!node || !node->isHTMLElement()) 94 return; 95 96 HTMLElement& element = toHTMLElement(*node); 97 RefPtrWillBeRawPtr<HTMLMenuElement> menuElement = element.contextMenu(); 98 if (!menuElement || !equalIgnoringCase(menuElement->fastGetAttribute(typeAttr), "popup")) 99 return; 100 RefPtrWillBeRawPtr<RelatedEvent> relatedEvent = RelatedEvent::create(EventTypeNames::show, true, true, node); 101 if (!menuElement->dispatchEvent(relatedEvent.release())) 102 return; 103 if (menuElement != element.contextMenu()) 104 return; 105 m_menuProvider = CustomContextMenuProvider::create(*menuElement, element); 106 m_menuProvider->populateContextMenu(m_contextMenu.get()); 107} 108 109void ContextMenuController::handleContextMenuEvent(Event* event) 110{ 111 m_contextMenu = createContextMenu(event); 112 if (!m_contextMenu) 113 return; 114 populateCustomContextMenu(*event); 115 showContextMenu(event); 116} 117 118void ContextMenuController::showContextMenu(Event* event, PassRefPtr<ContextMenuProvider> menuProvider) 119{ 120 m_menuProvider = menuProvider; 121 122 m_contextMenu = createContextMenu(event); 123 if (!m_contextMenu) { 124 clearContextMenu(); 125 return; 126 } 127 128 m_menuProvider->populateContextMenu(m_contextMenu.get()); 129 showContextMenu(event); 130} 131 132void ContextMenuController::showContextMenuAtPoint(LocalFrame* frame, float x, float y, PassRefPtr<ContextMenuProvider> menuProvider) 133{ 134 m_menuProvider = menuProvider; 135 136 LayoutPoint location(x, y); 137 m_contextMenu = createContextMenu(frame, location); 138 if (!m_contextMenu) { 139 clearContextMenu(); 140 return; 141 } 142 143 m_menuProvider->populateContextMenu(m_contextMenu.get()); 144 showContextMenu(nullptr); 145} 146 147PassOwnPtr<ContextMenu> ContextMenuController::createContextMenu(Event* event) 148{ 149 ASSERT(event); 150 151 if (!event->isMouseEvent()) 152 return nullptr; 153 154 MouseEvent* mouseEvent = toMouseEvent(event); 155 return createContextMenu(event->target()->toNode()->document().frame(), mouseEvent->absoluteLocation()); 156} 157 158PassOwnPtr<ContextMenu> ContextMenuController::createContextMenu(LocalFrame* frame, const LayoutPoint& location) 159{ 160 HitTestResult result(location); 161 162 if (frame) 163 result = frame->eventHandler().hitTestResultAtPoint(location, HitTestRequest::ReadOnly | HitTestRequest::Active); 164 165 if (!result.innerNonSharedNode()) 166 return nullptr; 167 168 m_hitTestResult = result; 169 170 return adoptPtr(new ContextMenu); 171} 172 173void ContextMenuController::showContextMenu(Event* event) 174{ 175 m_client->showContextMenu(m_contextMenu.get()); 176 if (event) 177 event->setDefaultHandled(); 178} 179 180void ContextMenuController::contextMenuItemSelected(const ContextMenuItem* item) 181{ 182 ASSERT(item->type() == ActionType || item->type() == CheckableActionType); 183 184 if (item->action() < ContextMenuItemBaseCustomTag || item->action() > ContextMenuItemLastCustomTag) 185 return; 186 187 ASSERT(m_menuProvider); 188 m_menuProvider->contextMenuItemSelected(item); 189} 190 191} // namespace blink 192