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