Home | History | Annotate | Download | only in WebCoreSupport
      1 /*
      2  * Copyright (C) 2006, 2007, 2008 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  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #import "WebContextMenuClient.h"
     30 
     31 #import "WebDelegateImplementationCaching.h"
     32 #import "WebElementDictionary.h"
     33 #import "WebFrame.h"
     34 #import "WebFrameInternal.h"
     35 #import "WebHTMLView.h"
     36 #import "WebHTMLViewInternal.h"
     37 #import "WebKitVersionChecks.h"
     38 #import "WebNSPasteboardExtras.h"
     39 #import "WebUIDelegate.h"
     40 #import "WebUIDelegatePrivate.h"
     41 #import "WebView.h"
     42 #import "WebViewFactory.h"
     43 #import "WebViewInternal.h"
     44 #import <WebCore/ContextMenu.h>
     45 #import <WebCore/KURL.h>
     46 #import <WebCore/RuntimeApplicationChecks.h>
     47 #import <WebKit/DOMPrivate.h>
     48 
     49 using namespace WebCore;
     50 
     51 @interface NSApplication (AppKitSecretsIKnowAbout)
     52 - (void)speakString:(NSString *)string;
     53 @end
     54 
     55 WebContextMenuClient::WebContextMenuClient(WebView *webView)
     56     : m_webView(webView)
     57 {
     58 }
     59 
     60 void WebContextMenuClient::contextMenuDestroyed()
     61 {
     62     delete this;
     63 }
     64 
     65 static BOOL isPreVersion3Client(void)
     66 {
     67     static BOOL preVersion3Client = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_3_0_CONTEXT_MENU_TAGS);
     68     return preVersion3Client;
     69 }
     70 
     71 static BOOL isPreInspectElementTagClient(void)
     72 {
     73     static BOOL preInspectElementTagClient = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_INSPECT_ELEMENT_MENU_TAG);
     74     return preInspectElementTagClient;
     75 }
     76 
     77 static NSMutableArray *fixMenusToSendToOldClients(NSMutableArray *defaultMenuItems)
     78 {
     79     NSMutableArray *savedItems = nil;
     80 
     81     unsigned defaultItemsCount = [defaultMenuItems count];
     82 
     83     if (isPreInspectElementTagClient() && defaultItemsCount >= 2) {
     84         NSMenuItem *secondToLastItem = [defaultMenuItems objectAtIndex:defaultItemsCount - 2];
     85         NSMenuItem *lastItem = [defaultMenuItems objectAtIndex:defaultItemsCount - 1];
     86 
     87         if ([secondToLastItem isSeparatorItem] && [lastItem tag] == WebMenuItemTagInspectElement) {
     88             savedItems = [NSMutableArray arrayWithCapacity:2];
     89             [savedItems addObject:secondToLastItem];
     90             [savedItems addObject:lastItem];
     91 
     92             [defaultMenuItems removeObject:secondToLastItem];
     93             [defaultMenuItems removeObject:lastItem];
     94             defaultItemsCount -= 2;
     95         }
     96     }
     97 
     98     BOOL preVersion3Client = isPreVersion3Client();
     99     if (!preVersion3Client)
    100         return savedItems;
    101 
    102     BOOL isMail = applicationIsAppleMail();
    103     for (unsigned i = 0; i < defaultItemsCount; ++i) {
    104         NSMenuItem *item = [defaultMenuItems objectAtIndex:i];
    105         int tag = [item tag];
    106         int oldStyleTag = tag;
    107 
    108         if (preVersion3Client && isMail && tag == WebMenuItemTagOpenLink) {
    109             // Tiger Mail changes our "Open Link in New Window" item to "Open Link"
    110             // and doesn't expect us to include an "Open Link" item at all. (5011905)
    111             [defaultMenuItems removeObjectAtIndex:i];
    112             i--;
    113             defaultItemsCount--;
    114             continue;
    115         }
    116 
    117         if (tag >= WEBMENUITEMTAG_WEBKIT_3_0_SPI_START) {
    118             // Change all editing-related SPI tags listed in WebUIDelegatePrivate.h to WebMenuItemTagOther
    119             // to match our old WebKit context menu behavior.
    120             oldStyleTag = WebMenuItemTagOther;
    121         } else {
    122             // All items are expected to have useful tags coming into this method.
    123             ASSERT(tag != WebMenuItemTagOther);
    124 
    125             // Use the pre-3.0 tags for the few items that changed tags as they moved from SPI to API. We
    126             // do this only for old clients; new Mail already expects the new symbols in this case.
    127             if (preVersion3Client) {
    128                 switch (tag) {
    129                     case WebMenuItemTagSearchInSpotlight:
    130                         oldStyleTag = OldWebMenuItemTagSearchInSpotlight;
    131                         break;
    132                     case WebMenuItemTagSearchWeb:
    133                         oldStyleTag = OldWebMenuItemTagSearchWeb;
    134                         break;
    135                     case WebMenuItemTagLookUpInDictionary:
    136                         oldStyleTag = OldWebMenuItemTagLookUpInDictionary;
    137                         break;
    138                     default:
    139                         break;
    140                 }
    141             }
    142         }
    143 
    144         if (oldStyleTag != tag)
    145             [item setTag:oldStyleTag];
    146     }
    147 
    148     return savedItems;
    149 }
    150 
    151 static void fixMenusReceivedFromOldClients(NSMutableArray *newMenuItems, NSMutableArray *savedItems)
    152 {
    153     if (savedItems)
    154         [newMenuItems addObjectsFromArray:savedItems];
    155 
    156     BOOL preVersion3Client = isPreVersion3Client();
    157     if (!preVersion3Client)
    158         return;
    159 
    160     // Restore the modern tags to the menu items whose tags we altered in fixMenusToSendToOldClients.
    161     unsigned newItemsCount = [newMenuItems count];
    162     for (unsigned i = 0; i < newItemsCount; ++i) {
    163         NSMenuItem *item = [newMenuItems objectAtIndex:i];
    164 
    165         int tag = [item tag];
    166         int modernTag = tag;
    167 
    168         if (tag == WebMenuItemTagOther) {
    169             // Restore the specific tag for items on which we temporarily set WebMenuItemTagOther to match old behavior.
    170             NSString *title = [item title];
    171             if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagOpenLink]])
    172                 modernTag = WebMenuItemTagOpenLink;
    173             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagIgnoreGrammar]])
    174                 modernTag = WebMenuItemTagIgnoreGrammar;
    175             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagSpellingMenu]])
    176                 modernTag = WebMenuItemTagSpellingMenu;
    177             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagShowSpellingPanel:true]]
    178                      || [title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagShowSpellingPanel:false]])
    179                 modernTag = WebMenuItemTagShowSpellingPanel;
    180             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagCheckSpelling]])
    181                 modernTag = WebMenuItemTagCheckSpelling;
    182             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagCheckSpellingWhileTyping]])
    183                 modernTag = WebMenuItemTagCheckSpellingWhileTyping;
    184             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagCheckGrammarWithSpelling]])
    185                 modernTag = WebMenuItemTagCheckGrammarWithSpelling;
    186             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagFontMenu]])
    187                 modernTag = WebMenuItemTagFontMenu;
    188             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagShowFonts]])
    189                 modernTag = WebMenuItemTagShowFonts;
    190             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagBold]])
    191                 modernTag = WebMenuItemTagBold;
    192             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagItalic]])
    193                 modernTag = WebMenuItemTagItalic;
    194             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagUnderline]])
    195                 modernTag = WebMenuItemTagUnderline;
    196             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagOutline]])
    197                 modernTag = WebMenuItemTagOutline;
    198             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagStyles]])
    199                 modernTag = WebMenuItemTagStyles;
    200             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagShowColors]])
    201                 modernTag = WebMenuItemTagShowColors;
    202             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagSpeechMenu]])
    203                 modernTag = WebMenuItemTagSpeechMenu;
    204             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagStartSpeaking]])
    205                 modernTag = WebMenuItemTagStartSpeaking;
    206             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagStopSpeaking]])
    207                 modernTag = WebMenuItemTagStopSpeaking;
    208             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagWritingDirectionMenu]])
    209                 modernTag = WebMenuItemTagWritingDirectionMenu;
    210             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagDefaultDirection]])
    211                 modernTag = WebMenuItemTagDefaultDirection;
    212             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagLeftToRight]])
    213                 modernTag = WebMenuItemTagLeftToRight;
    214             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagRightToLeft]])
    215                 modernTag = WebMenuItemTagRightToLeft;
    216             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagCorrectSpellingAutomatically]])
    217                 modernTag = WebMenuItemTagCorrectSpellingAutomatically;
    218             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagSubstitutionsMenu]])
    219                 modernTag = WebMenuItemTagSubstitutionsMenu;
    220             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagShowSubstitutions:true]]
    221                      || [title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagShowSubstitutions:false]])
    222                 modernTag = WebMenuItemTagShowSubstitutions;
    223             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagSmartCopyPaste]])
    224                 modernTag = WebMenuItemTagSmartCopyPaste;
    225             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagSmartQuotes]])
    226                 modernTag = WebMenuItemTagSmartQuotes;
    227             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagSmartDashes]])
    228                 modernTag = WebMenuItemTagSmartDashes;
    229             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagSmartLinks]])
    230                 modernTag = WebMenuItemTagSmartLinks;
    231             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagTextReplacement]])
    232                 modernTag = WebMenuItemTagTextReplacement;
    233             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagTransformationsMenu]])
    234                 modernTag = WebMenuItemTagTransformationsMenu;
    235             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagMakeUpperCase]])
    236                 modernTag = WebMenuItemTagMakeUpperCase;
    237             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagMakeLowerCase]])
    238                 modernTag = WebMenuItemTagMakeLowerCase;
    239             else if ([title isEqualToString:[[WebViewFactory sharedFactory] contextMenuItemTagCapitalize]])
    240                 modernTag = WebMenuItemTagCapitalize;
    241             else {
    242             // We don't expect WebMenuItemTagOther for any items other than the ones we explicitly handle.
    243             // There's nothing to prevent an app from applying this tag, but they are supposed to only
    244             // use tags in the range starting with WebMenuItemBaseApplicationTag=10000
    245                 ASSERT_NOT_REACHED();
    246             }
    247         } else if (preVersion3Client) {
    248             // Restore the new API tag for items on which we temporarily set the old SPI tag. The old SPI tag was
    249             // needed to avoid confusing clients linked against earlier WebKits; the new API tag is needed for
    250             // WebCore to handle the menu items appropriately (without needing to know about the old SPI tags).
    251             switch (tag) {
    252                 case OldWebMenuItemTagSearchInSpotlight:
    253                     modernTag = WebMenuItemTagSearchInSpotlight;
    254                     break;
    255                 case OldWebMenuItemTagSearchWeb:
    256                     modernTag = WebMenuItemTagSearchWeb;
    257                     break;
    258                 case OldWebMenuItemTagLookUpInDictionary:
    259                     modernTag = WebMenuItemTagLookUpInDictionary;
    260                     break;
    261                 default:
    262                     break;
    263             }
    264         }
    265 
    266         if (modernTag != tag)
    267             [item setTag:modernTag];
    268     }
    269 }
    270 
    271 NSMutableArray* WebContextMenuClient::getCustomMenuFromDefaultItems(ContextMenu* defaultMenu)
    272 {
    273     id delegate = [m_webView UIDelegate];
    274     SEL selector = @selector(webView:contextMenuItemsForElement:defaultMenuItems:);
    275     if (![delegate respondsToSelector:selector])
    276         return defaultMenu->platformDescription();
    277 
    278     NSDictionary *element = [[[WebElementDictionary alloc] initWithHitTestResult:defaultMenu->hitTestResult()] autorelease];
    279 
    280     BOOL preVersion3Client = isPreVersion3Client();
    281     if (preVersion3Client) {
    282         DOMNode *node = [element objectForKey:WebElementDOMNodeKey];
    283         if ([node isKindOfClass:[DOMHTMLInputElement class]] && [(DOMHTMLInputElement *)node _isTextField])
    284             return defaultMenu->platformDescription();
    285         if ([node isKindOfClass:[DOMHTMLTextAreaElement class]])
    286             return defaultMenu->platformDescription();
    287     }
    288 
    289     NSMutableArray *defaultMenuItems = defaultMenu->platformDescription();
    290 
    291     unsigned defaultItemsCount = [defaultMenuItems count];
    292     for (unsigned i = 0; i < defaultItemsCount; ++i)
    293         [[defaultMenuItems objectAtIndex:i] setRepresentedObject:element];
    294 
    295     NSMutableArray *savedItems = [fixMenusToSendToOldClients(defaultMenuItems) retain];
    296     NSArray *delegateSuppliedItems = CallUIDelegate(m_webView, selector, element, defaultMenuItems);
    297     NSMutableArray *newMenuItems = [delegateSuppliedItems mutableCopy];
    298     fixMenusReceivedFromOldClients(newMenuItems, savedItems);
    299     [savedItems release];
    300     return [newMenuItems autorelease];
    301 }
    302 
    303 void WebContextMenuClient::contextMenuItemSelected(ContextMenuItem* item, const ContextMenu* parentMenu)
    304 {
    305     id delegate = [m_webView UIDelegate];
    306     SEL selector = @selector(webView:contextMenuItemSelected:forElement:);
    307     if ([delegate respondsToSelector:selector]) {
    308         NSDictionary *element = [[WebElementDictionary alloc] initWithHitTestResult:parentMenu->hitTestResult()];
    309         NSMenuItem *platformItem = item->releasePlatformDescription();
    310 
    311         CallUIDelegate(m_webView, selector, platformItem, element);
    312 
    313         [element release];
    314         [platformItem release];
    315     }
    316 }
    317 
    318 void WebContextMenuClient::downloadURL(const KURL& url)
    319 {
    320     [m_webView _downloadURL:url];
    321 }
    322 
    323 void WebContextMenuClient::searchWithSpotlight()
    324 {
    325     [m_webView _searchWithSpotlightFromMenu:nil];
    326 }
    327 
    328 void WebContextMenuClient::searchWithGoogle(const Frame*)
    329 {
    330     [m_webView _searchWithGoogleFromMenu:nil];
    331 }
    332 
    333 void WebContextMenuClient::lookUpInDictionary(Frame* frame)
    334 {
    335     WebHTMLView* htmlView = (WebHTMLView*)[[kit(frame) frameView] documentView];
    336     if(![htmlView isKindOfClass:[WebHTMLView class]])
    337         return;
    338     [htmlView _lookUpInDictionaryFromMenu:nil];
    339 }
    340 
    341 bool WebContextMenuClient::isSpeaking()
    342 {
    343     return [NSApp isSpeaking];
    344 }
    345 
    346 void WebContextMenuClient::speak(const String& string)
    347 {
    348     [NSApp speakString:[[(NSString*)string copy] autorelease]];
    349 }
    350 
    351 void WebContextMenuClient::stopSpeaking()
    352 {
    353     [NSApp stopSpeaking:nil];
    354 }
    355