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 44 namespace blink { 45 46 using namespace HTMLNames; 47 48 ContextMenuController::ContextMenuController(Page*, ContextMenuClient* client) 49 : m_client(client) 50 { 51 ASSERT_ARG(client, client); 52 } 53 54 ContextMenuController::~ContextMenuController() 55 { 56 } 57 58 PassOwnPtrWillBeRawPtr<ContextMenuController> ContextMenuController::create(Page* page, ContextMenuClient* client) 59 { 60 return adoptPtrWillBeNoop(new ContextMenuController(page, client)); 61 } 62 63 void ContextMenuController::trace(Visitor* visitor) 64 { 65 visitor->trace(m_hitTestResult); 66 } 67 68 void 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 78 void 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 87 void 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 109 void ContextMenuController::handleContextMenuEvent(Event* event) 110 { 111 m_contextMenu = createContextMenu(event); 112 if (!m_contextMenu) 113 return; 114 populateCustomContextMenu(*event); 115 showContextMenu(event); 116 } 117 118 void 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 132 void 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 147 PassOwnPtr<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 158 PassOwnPtr<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 173 void ContextMenuController::showContextMenu(Event* event) 174 { 175 m_client->showContextMenu(m_contextMenu.get()); 176 if (event) 177 event->setDefaultHandled(); 178 } 179 180 void 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