Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2010 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #import "config.h"
     27 #import "WebContextMenuProxyMac.h"
     28 
     29 #import "PageClientImpl.h"
     30 #import "WebContextMenuItemData.h"
     31 #import "WKView.h"
     32 
     33 #import <WebCore/IntRect.h>
     34 #import <WebKitSystemInterface.h>
     35 
     36 using namespace WebCore;
     37 
     38 @interface WebUserDataWrapper : NSObject {
     39     RefPtr<WebKit::APIObject> _webUserData;
     40 }
     41 - (id)initWithUserData:(WebKit::APIObject*)userData;
     42 - (WebKit::APIObject*)userData;
     43 @end
     44 
     45 @implementation WebUserDataWrapper
     46 
     47 - (id)initWithUserData:(WebKit::APIObject*)userData
     48 {
     49     self = [super init];
     50     if (!self)
     51         return nil;
     52 
     53     _webUserData = userData;
     54     return self;
     55 }
     56 
     57 - (WebKit::APIObject*)userData
     58 {
     59     return _webUserData.get();
     60 }
     61 
     62 @end
     63 
     64 @interface WKMenuTarget : NSObject {
     65     WebKit::WebContextMenuProxyMac* _menuProxy;
     66 }
     67 + (WKMenuTarget*)sharedMenuTarget;
     68 - (WebKit::WebContextMenuProxyMac*)menuProxy;
     69 - (void)setMenuProxy:(WebKit::WebContextMenuProxyMac*)menuProxy;
     70 - (void)forwardContextMenuAction:(id)sender;
     71 @end
     72 
     73 @implementation WKMenuTarget
     74 
     75 + (WKMenuTarget*)sharedMenuTarget
     76 {
     77     static WKMenuTarget* target = [[WKMenuTarget alloc] init];
     78     return target;
     79 }
     80 
     81 - (WebKit::WebContextMenuProxyMac*)menuProxy
     82 {
     83     return _menuProxy;
     84 }
     85 
     86 - (void)setMenuProxy:(WebKit::WebContextMenuProxyMac*)menuProxy
     87 {
     88     _menuProxy = menuProxy;
     89 }
     90 
     91 - (void)forwardContextMenuAction:(id)sender
     92 {
     93     WebKit::WebContextMenuItemData item(ActionType, static_cast<ContextMenuAction>([sender tag]), [sender title], [sender isEnabled], [sender state] == NSOnState);
     94 
     95     if (id representedObject = [sender representedObject]) {
     96         ASSERT([representedObject isKindOfClass:[WebUserDataWrapper class]]);
     97         item.setUserData([static_cast<WebUserDataWrapper *>(representedObject) userData]);
     98     }
     99 
    100     _menuProxy->contextMenuItemSelected(item);
    101 }
    102 
    103 @end
    104 
    105 namespace WebKit {
    106 
    107 WebContextMenuProxyMac::WebContextMenuProxyMac(WKView* webView, WebPageProxy* page)
    108     : m_webView(webView)
    109     , m_page(page)
    110 {
    111 }
    112 
    113 WebContextMenuProxyMac::~WebContextMenuProxyMac()
    114 {
    115     if (m_popup)
    116         [m_popup.get() setControlView:nil];
    117 }
    118 
    119 void WebContextMenuProxyMac::contextMenuItemSelected(const WebContextMenuItemData& item)
    120 {
    121     m_page->contextMenuItemSelected(item);
    122 }
    123 
    124 static void populateNSMenu(NSMenu* menu, const Vector<RetainPtr<NSMenuItem> >& menuItemVector)
    125 {
    126     for (unsigned i = 0; i < menuItemVector.size(); ++i) {
    127         NSInteger oldState = [menuItemVector[i].get() state];
    128         [menu addItem:menuItemVector[i].get()];
    129         [menuItemVector[i].get() setState:oldState];
    130     }
    131 }
    132 
    133 static Vector<RetainPtr<NSMenuItem> > nsMenuItemVector(const Vector<WebContextMenuItemData>& items)
    134 {
    135     Vector<RetainPtr<NSMenuItem> > result;
    136 
    137     unsigned size = items.size();
    138     result.reserveCapacity(size);
    139     for (unsigned i = 0; i < size; i++) {
    140         switch (items[i].type()) {
    141         case ActionType:
    142         case CheckableActionType: {
    143             NSMenuItem* menuItem = [[NSMenuItem alloc] initWithTitle:nsStringFromWebCoreString(items[i].title()) action:@selector(forwardContextMenuAction:) keyEquivalent:@""];
    144             [menuItem setTag:items[i].action()];
    145             [menuItem setEnabled:items[i].enabled()];
    146             [menuItem setState:items[i].checked() ? NSOnState : NSOffState];
    147 
    148             if (items[i].userData()) {
    149                 WebUserDataWrapper *wrapper = [[WebUserDataWrapper alloc] initWithUserData:items[i].userData()];
    150                 [menuItem setRepresentedObject:wrapper];
    151                 [wrapper release];
    152             }
    153 
    154             result.append(RetainPtr<NSMenuItem>(AdoptNS, menuItem));
    155             break;
    156         }
    157         case SeparatorType:
    158             result.append([NSMenuItem separatorItem]);
    159             break;
    160         case SubmenuType: {
    161             NSMenu* menu = [[NSMenu alloc] initWithTitle:nsStringFromWebCoreString(items[i].title())];
    162             [menu setAutoenablesItems:NO];
    163             populateNSMenu(menu, nsMenuItemVector(items[i].submenu()));
    164 
    165             NSMenuItem* menuItem = [[NSMenuItem alloc] initWithTitle:nsStringFromWebCoreString(items[i].title()) action:@selector(forwardContextMenuAction:) keyEquivalent:@""];
    166             [menuItem setEnabled:items[i].enabled()];
    167             [menuItem setSubmenu:menu];
    168             [menu release];
    169 
    170             result.append(RetainPtr<NSMenuItem>(AdoptNS, menuItem));
    171 
    172             break;
    173         }
    174         default:
    175             ASSERT_NOT_REACHED();
    176         }
    177     }
    178 
    179     WKMenuTarget* target = [WKMenuTarget sharedMenuTarget];
    180     for (unsigned i = 0; i < size; ++i)
    181         [result[i].get() setTarget:target];
    182 
    183     return result;
    184 }
    185 
    186 void WebContextMenuProxyMac::populate(const Vector<WebContextMenuItemData>& items)
    187 {
    188     if (m_popup)
    189         [m_popup.get() removeAllItems];
    190     else {
    191         m_popup.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
    192         [m_popup.get() setUsesItemFromMenu:NO];
    193         [m_popup.get() setAutoenablesItems:NO];
    194     }
    195 
    196     NSMenu* menu = [m_popup.get() menu];
    197     populateNSMenu(menu, nsMenuItemVector(items));
    198 }
    199 
    200 void WebContextMenuProxyMac::showContextMenu(const IntPoint& menuLocation, const Vector<WebContextMenuItemData>& items)
    201 {
    202     if (items.isEmpty())
    203         return;
    204 
    205     populate(items);
    206     [[WKMenuTarget sharedMenuTarget] setMenuProxy:this];
    207 
    208     NSRect menuRect = NSMakeRect(menuLocation.x(), menuLocation.y(), 0, 0);
    209 
    210     [m_popup.get() attachPopUpWithFrame:menuRect inView:m_webView];
    211 
    212     NSMenu* menu = [m_popup.get() menu];
    213 
    214     // These values were borrowed from AppKit to match their placement of the menu.
    215     NSRect titleFrame = [m_popup.get()  titleRectForBounds:menuRect];
    216     if (titleFrame.size.width <= 0 || titleFrame.size.height <= 0)
    217         titleFrame = menuRect;
    218     float vertOffset = roundf((NSMaxY(menuRect) - NSMaxY(titleFrame)) + NSHeight(titleFrame));
    219     NSPoint location = NSMakePoint(NSMinX(menuRect), NSMaxY(menuRect) - vertOffset);
    220 
    221     location = [m_webView convertPoint:location toView:nil];
    222     location = [m_webView.window convertBaseToScreen:location];
    223 
    224     WKPopupContextMenu(menu, location);
    225 
    226     [m_popup.get() dismissPopUp];
    227 }
    228 
    229 void WebContextMenuProxyMac::hideContextMenu()
    230 {
    231     [m_popup.get() dismissPopUp];
    232 }
    233 
    234 } // namespace WebKit
    235