1/* 2 * Copyright (C) 2010, 2011 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 "WebPopupMenuProxyMac.h" 28 29#import "NativeWebMouseEvent.h" 30#import "PageClientImpl.h" 31#import "PlatformPopupMenuData.h" 32#import "WKView.h" 33#import "WebPopupItem.h" 34#import <WebKitSystemInterface.h> 35 36using namespace WebCore; 37 38namespace WebKit { 39 40WebPopupMenuProxyMac::WebPopupMenuProxyMac(WKView *webView, WebPopupMenuProxy::Client* client) 41 : WebPopupMenuProxy(client) 42 , m_webView(webView) 43{ 44} 45 46WebPopupMenuProxyMac::~WebPopupMenuProxyMac() 47{ 48 if (m_popup) 49 [m_popup.get() setControlView:nil]; 50} 51 52void WebPopupMenuProxyMac::populate(const Vector<WebPopupItem>& items, NSFont *font, TextDirection menuTextDirection) 53{ 54 if (m_popup) 55 [m_popup.get() removeAllItems]; 56 else { 57 m_popup.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]); 58 [m_popup.get() setUsesItemFromMenu:NO]; 59 [m_popup.get() setAutoenablesItems:NO]; 60 } 61 62 int size = items.size(); 63 64 for (int i = 0; i < size; i++) { 65 if (items[i].m_type == WebPopupItem::Separator) 66 [[m_popup.get() menu] addItem:[NSMenuItem separatorItem]]; 67 else { 68 [m_popup.get() addItemWithTitle:@""]; 69 NSMenuItem *menuItem = [m_popup.get() lastItem]; 70 71 RetainPtr<NSMutableParagraphStyle> paragraphStyle(AdoptNS, [[NSParagraphStyle defaultParagraphStyle] mutableCopy]); 72 NSWritingDirection writingDirection = items[i].m_textDirection == LTR ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft; 73 [paragraphStyle.get() setBaseWritingDirection:writingDirection]; 74 [paragraphStyle.get() setAlignment:menuTextDirection == LTR ? NSLeftTextAlignment : NSRightTextAlignment]; 75 RetainPtr<NSMutableDictionary> attributes(AdoptNS, [[NSMutableDictionary alloc] initWithObjectsAndKeys: 76 paragraphStyle.get(), NSParagraphStyleAttributeName, 77 font, NSFontAttributeName, 78 nil]); 79 if (items[i].m_hasTextDirectionOverride) { 80 RetainPtr<NSNumber> writingDirectionValue(AdoptNS, [[NSNumber alloc] initWithInteger:writingDirection + NSTextWritingDirectionOverride]); 81 RetainPtr<NSArray> writingDirectionArray(AdoptNS, [[NSArray alloc] initWithObjects:writingDirectionValue.get(), nil]); 82 [attributes.get() setObject:writingDirectionArray.get() forKey:NSWritingDirectionAttributeName]; 83 } 84 RetainPtr<NSAttributedString> string(AdoptNS, [[NSAttributedString alloc] initWithString:nsStringFromWebCoreString(items[i].m_text) attributes:attributes.get()]); 85 86 [menuItem setAttributedTitle:string.get()]; 87 [menuItem setEnabled:items[i].m_isEnabled]; 88 [menuItem setToolTip:nsStringFromWebCoreString(items[i].m_toolTip)]; 89 } 90 } 91} 92 93void WebPopupMenuProxyMac::showPopupMenu(const IntRect& rect, TextDirection textDirection, double scaleFactor, const Vector<WebPopupItem>& items, const PlatformPopupMenuData& data, int32_t selectedIndex) 94{ 95 NSFont *font; 96 if (data.fontInfo.fontAttributeDictionary) { 97 NSFontDescriptor *fontDescriptor = [NSFontDescriptor fontDescriptorWithFontAttributes:(NSDictionary *)data.fontInfo.fontAttributeDictionary.get()]; 98 font = [NSFont fontWithDescriptor:fontDescriptor size:((scaleFactor != 1) ? [fontDescriptor pointSize] * scaleFactor : 0)]; 99 } else 100 font = [NSFont menuFontOfSize:0]; 101 102 populate(items, font, textDirection); 103 104 [m_popup.get() attachPopUpWithFrame:rect inView:m_webView]; 105 [m_popup.get() selectItemAtIndex:selectedIndex]; 106 [m_popup.get() setUserInterfaceLayoutDirection:textDirection == LTR ? NSUserInterfaceLayoutDirectionLeftToRight : NSUserInterfaceLayoutDirectionRightToLeft]; 107 108 NSMenu *menu = [m_popup.get() menu]; 109 110 // These values were borrowed from AppKit to match their placement of the menu. 111 const int popOverHorizontalAdjust = -10; 112 const int popUnderHorizontalAdjust = 6; 113 const int popUnderVerticalAdjust = 6; 114 115 // Menus that pop-over directly obscure the node that generated the popup menu. 116 // Menus that pop-under are offset underneath it. 117 NSPoint location; 118 if (data.shouldPopOver) { 119 NSRect titleFrame = [m_popup.get() titleRectForBounds:rect]; 120 if (titleFrame.size.width <= 0 || titleFrame.size.height <= 0) 121 titleFrame = rect; 122 float vertOffset = roundf((NSMaxY(rect) - NSMaxY(titleFrame)) + NSHeight(titleFrame)); 123 location = NSMakePoint(NSMinX(rect) + popOverHorizontalAdjust, NSMaxY(rect) - vertOffset); 124 } else 125 location = NSMakePoint(NSMinX(rect) + popUnderHorizontalAdjust, NSMaxY(rect) + popUnderVerticalAdjust); 126 127 RetainPtr<NSView> dummyView(AdoptNS, [[NSView alloc] initWithFrame:rect]); 128 [m_webView addSubview:dummyView.get()]; 129 location = [dummyView.get() convertPoint:location fromView:m_webView]; 130 131 WKPopupMenu(menu, location, roundf(NSWidth(rect)), dummyView.get(), selectedIndex, font); 132 133 [m_popup.get() dismissPopUp]; 134 [dummyView.get() removeFromSuperview]; 135 136 if (!m_client) 137 return; 138 139 m_client->valueChangedForPopupMenu(this, [m_popup.get() indexOfSelectedItem]); 140 141 // <https://bugs.webkit.org/show_bug.cgi?id=57904> This code is adopted from EventHandler::sendFakeEventsAfterWidgetTracking(). 142 if (!m_client->currentlyProcessedMouseDownEvent()) 143 return; 144 145 NSEvent* initiatingNSEvent = m_client->currentlyProcessedMouseDownEvent()->nativeEvent(); 146 if ([initiatingNSEvent type] != NSLeftMouseDown) 147 return; 148 149 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp 150 location:[initiatingNSEvent locationInWindow] 151 modifierFlags:[initiatingNSEvent modifierFlags] 152 timestamp:[initiatingNSEvent timestamp] 153 windowNumber:[initiatingNSEvent windowNumber] 154 context:[initiatingNSEvent context] 155 eventNumber:[initiatingNSEvent eventNumber] 156 clickCount:[initiatingNSEvent clickCount] 157 pressure:[initiatingNSEvent pressure]]; 158 159 [NSApp postEvent:fakeEvent atStart:YES]; 160 fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved 161 location:[[m_webView window] convertScreenToBase:[NSEvent mouseLocation]] 162 modifierFlags:[initiatingNSEvent modifierFlags] 163 timestamp:[initiatingNSEvent timestamp] 164 windowNumber:[initiatingNSEvent windowNumber] 165 context:[initiatingNSEvent context] 166 eventNumber:0 167 clickCount:0 168 pressure:0]; 169 [NSApp postEvent:fakeEvent atStart:YES]; 170} 171 172void WebPopupMenuProxyMac::hidePopupMenu() 173{ 174 [m_popup.get() dismissPopUp]; 175} 176 177} // namespace WebKit 178