Home | History | Annotate | Download | only in page
      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