diff options
Diffstat (limited to 'WebKit/mac/WebCoreSupport/WebContextMenuClient.mm')
-rw-r--r-- | WebKit/mac/WebCoreSupport/WebContextMenuClient.mm | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/WebKit/mac/WebCoreSupport/WebContextMenuClient.mm b/WebKit/mac/WebCoreSupport/WebContextMenuClient.mm new file mode 100644 index 0000000..6b5ad14 --- /dev/null +++ b/WebKit/mac/WebCoreSupport/WebContextMenuClient.mm @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "WebContextMenuClient.h" + +#import "WebElementDictionary.h" +#import "WebFrame.h" +#import "WebFrameInternal.h" +#import "WebHTMLView.h" +#import "WebHTMLViewInternal.h" +#import "WebKitVersionChecks.h" +#import "WebNSPasteboardExtras.h" +#import "WebUIDelegate.h" +#import "WebUIDelegatePrivate.h" +#import "WebView.h" +#import "WebViewFactory.h" +#import "WebViewInternal.h" +#import <WebCore/ContextMenu.h> +#import <WebCore/KURL.h> +#import <WebKit/DOMPrivate.h> + +using namespace WebCore; + +@interface NSApplication (AppKitSecretsIKnowAbout) +- (void)speakString:(NSString *)string; +@end + +WebContextMenuClient::WebContextMenuClient(WebView *webView) + : m_webView(webView) +{ +} + +void WebContextMenuClient::contextMenuDestroyed() +{ + delete this; +} + +static BOOL isAppleMail(void) +{ + return [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.mail"]; +} + +static BOOL isPreVersion3Client(void) +{ + static BOOL preVersion3Client = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_3_0_CONTEXT_MENU_TAGS); + return preVersion3Client; +} + +static BOOL isPreInspectElementTagClient(void) +{ + static BOOL preInspectElementTagClient = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_INSPECT_ELEMENT_MENU_TAG); + return preInspectElementTagClient; +} + +static NSMutableArray *fixMenusToSendToOldClients(NSMutableArray *defaultMenuItems) +{ + NSMutableArray *savedItems = nil; + + unsigned defaultItemsCount = [defaultMenuItems count]; + + if (isPreInspectElementTagClient() && defaultItemsCount >= 2) { + NSMenuItem *secondToLastItem = [defaultMenuItems objectAtIndex:defaultItemsCount - 2]; + NSMenuItem *lastItem = [defaultMenuItems objectAtIndex:defaultItemsCount - 1]; + + if ([secondToLastItem isSeparatorItem] && [lastItem tag] == WebMenuItemTagInspectElement) { + savedItems = [NSMutableArray arrayWithCapacity:2]; + [savedItems addObject:secondToLastItem]; + [savedItems addObject:lastItem]; + + [defaultMenuItems removeObject:secondToLastItem]; + [defaultMenuItems removeObject:lastItem]; + defaultItemsCount -= 2; + } + } + + BOOL preVersion3Client = isPreVersion3Client(); + if (!preVersion3Client) + return savedItems; + + BOOL isMail = isAppleMail(); + for (unsigned i = 0; i < defaultItemsCount; ++i) { + NSMenuItem *item = [defaultMenuItems objectAtIndex:i]; + int tag = [item tag]; + int oldStyleTag = tag; + + if (preVersion3Client && isMail && tag == WebMenuItemTagOpenLink) { + // Tiger Mail changes our "Open Link in New Window" item to "Open Link" + // and doesn't expect us to include an "Open Link" item at all. (5011905) + [defaultMenuItems removeObjectAtIndex:i]; + i--; + defaultItemsCount--; + continue; + } + + if (tag >= WEBMENUITEMTAG_WEBKIT_3_0_SPI_START) { + // Change all editing-related SPI tags listed in WebUIDelegatePrivate.h to WebMenuItemTagOther + // to match our old WebKit context menu behavior. + oldStyleTag = WebMenuItemTagOther; + } else { + // All items are expected to have useful tags coming into this method. + ASSERT(tag != WebMenuItemTagOther); + + // Use the pre-3.0 tags for the few items that changed tags as they moved from SPI to API. We + // do this only for old clients; new Mail already expects the new symbols in this case. + if (preVersion3Client) { + switch (tag) { + case WebMenuItemTagSearchInSpotlight: + oldStyleTag = OldWebMenuItemTagSearchInSpotlight; + break; + case WebMenuItemTagSearchWeb: + oldStyleTag = OldWebMenuItemTagSearchWeb; + break; + case WebMenuItemTagLookUpInDictionary: + oldStyleTag = OldWebMenuItemTagLookUpInDictionary; + break; + default: + break; + } + } + } + + if (oldStyleTag != tag) + [item setTag:oldStyleTag]; + } + + return savedItems; +} + +static void fixMenusReceivedFromOldClients(NSMutableArray *newMenuItems, NSMutableArray *savedItems) +{ + if (savedItems) + [newMenuItems addObjectsFromArray:savedItems]; + + BOOL preVersion3Client = isPreVersion3Client(); + if (!preVersion3Client) + return; + + // Restore the modern tags to the menu items whose tags we altered in fixMenusToSendToOldClients. + unsigned newItemsCount = [newMenuItems count]; + for (unsigned i = 0; i < newItemsCount; ++i) { + NSMenuItem *item = [newMenuItems objectAtIndex:i]; + + int tag = [item tag]; + int modernTag = tag; + + if (tag == WebMenuItemTagOther) { + // Restore the specific tag for items on which we temporarily set WebMenuItemTagOther to match old behavior. + NSString *title = [item title]; + if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagOpenLink]]) + modernTag = WebMenuItemTagOpenLink; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagIgnoreGrammar]]) + modernTag = WebMenuItemTagIgnoreGrammar; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagSpellingMenu]]) + modernTag = WebMenuItemTagSpellingMenu; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagShowSpellingPanel:true]] + || [title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagShowSpellingPanel:false]]) + modernTag = WebMenuItemTagShowSpellingPanel; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagCheckSpelling]]) + modernTag = WebMenuItemTagCheckSpelling; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagCheckSpellingWhileTyping]]) + modernTag = WebMenuItemTagCheckSpellingWhileTyping; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagCheckGrammarWithSpelling]]) + modernTag = WebMenuItemTagCheckGrammarWithSpelling; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagFontMenu]]) + modernTag = WebMenuItemTagFontMenu; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagShowFonts]]) + modernTag = WebMenuItemTagShowFonts; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagBold]]) + modernTag = WebMenuItemTagBold; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagItalic]]) + modernTag = WebMenuItemTagItalic; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagUnderline]]) + modernTag = WebMenuItemTagUnderline; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagOutline]]) + modernTag = WebMenuItemTagOutline; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagStyles]]) + modernTag = WebMenuItemTagStyles; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagShowColors]]) + modernTag = WebMenuItemTagShowColors; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagSpeechMenu]]) + modernTag = WebMenuItemTagSpeechMenu; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagStartSpeaking]]) + modernTag = WebMenuItemTagStartSpeaking; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagStopSpeaking]]) + modernTag = WebMenuItemTagStopSpeaking; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagWritingDirectionMenu]]) + modernTag = WebMenuItemTagWritingDirectionMenu; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagDefaultDirection]]) + modernTag = WebMenuItemTagDefaultDirection; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagLeftToRight]]) + modernTag = WebMenuItemTagLeftToRight; + else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagRightToLeft]]) + modernTag = WebMenuItemTagRightToLeft; + else { + // We don't expect WebMenuItemTagOther for any items other than the ones we explicitly handle. + // There's nothing to prevent an app from applying this tag, but they are supposed to only + // use tags in the range starting with WebMenuItemBaseApplicationTag=10000 + ASSERT_NOT_REACHED(); + } + } else if (preVersion3Client) { + // Restore the new API tag for items on which we temporarily set the old SPI tag. The old SPI tag was + // needed to avoid confusing clients linked against earlier WebKits; the new API tag is needed for + // WebCore to handle the menu items appropriately (without needing to know about the old SPI tags). + switch (tag) { + case OldWebMenuItemTagSearchInSpotlight: + modernTag = WebMenuItemTagSearchInSpotlight; + break; + case OldWebMenuItemTagSearchWeb: + modernTag = WebMenuItemTagSearchWeb; + break; + case OldWebMenuItemTagLookUpInDictionary: + modernTag = WebMenuItemTagLookUpInDictionary; + break; + default: + break; + } + } + + if (modernTag != tag) + [item setTag:modernTag]; + } +} + +NSMutableArray* WebContextMenuClient::getCustomMenuFromDefaultItems(ContextMenu* defaultMenu) +{ + id delegate = [m_webView UIDelegate]; + SEL selector = @selector(webView:contextMenuItemsForElement:defaultMenuItems:); + if (![delegate respondsToSelector:selector]) + return defaultMenu->platformDescription(); + + NSDictionary *element = [[[WebElementDictionary alloc] initWithHitTestResult:defaultMenu->hitTestResult()] autorelease]; + + BOOL preVersion3Client = isPreVersion3Client(); + if (preVersion3Client) { + DOMNode *node = [element objectForKey:WebElementDOMNodeKey]; + if ([node isKindOfClass:[DOMHTMLInputElement class]] && [(DOMHTMLInputElement *)node _isTextField]) + return defaultMenu->platformDescription(); + if ([node isKindOfClass:[DOMHTMLTextAreaElement class]]) + return defaultMenu->platformDescription(); + } + + NSMutableArray *defaultMenuItems = defaultMenu->platformDescription(); + + unsigned defaultItemsCount = [defaultMenuItems count]; + for (unsigned i = 0; i < defaultItemsCount; ++i) + [[defaultMenuItems objectAtIndex:i] setRepresentedObject:element]; + + NSMutableArray *savedItems = [fixMenusToSendToOldClients(defaultMenuItems) retain]; + NSArray *delegateSuppliedItems = CallUIDelegate(m_webView, selector, element, defaultMenuItems); + NSMutableArray *newMenuItems = [delegateSuppliedItems mutableCopy]; + fixMenusReceivedFromOldClients(newMenuItems, savedItems); + [savedItems release]; + return [newMenuItems autorelease]; +} + +void WebContextMenuClient::contextMenuItemSelected(ContextMenuItem* item, const ContextMenu* parentMenu) +{ + id delegate = [m_webView UIDelegate]; + SEL selector = @selector(webView:contextMenuItemSelected:forElement:); + if ([delegate respondsToSelector:selector]) { + NSDictionary *element = [[WebElementDictionary alloc] initWithHitTestResult:parentMenu->hitTestResult()]; + NSMenuItem *platformItem = item->releasePlatformDescription(); + + CallUIDelegate(m_webView, selector, platformItem, element); + + [element release]; + [platformItem release]; + } +} + +void WebContextMenuClient::downloadURL(const KURL& url) +{ + [m_webView _downloadURL:url]; +} + +void WebContextMenuClient::searchWithSpotlight() +{ + [m_webView _searchWithSpotlightFromMenu:nil]; +} + +void WebContextMenuClient::searchWithGoogle(const Frame*) +{ + [m_webView _searchWithGoogleFromMenu:nil]; +} + +void WebContextMenuClient::lookUpInDictionary(Frame* frame) +{ + WebHTMLView* htmlView = (WebHTMLView*)[[kit(frame) frameView] documentView]; + if(![htmlView isKindOfClass:[WebHTMLView class]]) + return; + [htmlView _lookUpInDictionaryFromMenu:nil]; +} + +void WebContextMenuClient::speak(const String& string) +{ + [NSApp speakString:[[(NSString*)string copy] autorelease]]; +} + +void WebContextMenuClient::stopSpeaking() +{ + [NSApp stopSpeaking]; +} |