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 36using 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 105namespace WebKit { 106 107WebContextMenuProxyMac::WebContextMenuProxyMac(WKView* webView, WebPageProxy* page) 108 : m_webView(webView) 109 , m_page(page) 110{ 111} 112 113WebContextMenuProxyMac::~WebContextMenuProxyMac() 114{ 115 if (m_popup) 116 [m_popup.get() setControlView:nil]; 117} 118 119void WebContextMenuProxyMac::contextMenuItemSelected(const WebContextMenuItemData& item) 120{ 121 m_page->contextMenuItemSelected(item); 122} 123 124static 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 133static 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 186void 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 200void 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 229void WebContextMenuProxyMac::hideContextMenu() 230{ 231 [m_popup.get() dismissPopUp]; 232} 233 234} // namespace WebKit 235