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