8587dae43c356bc23eac9f2315bd5ec8ac9fb64a
[WebKit-https.git] / WebKit / WebView.subproj / WebDefaultContextMenuDelegate.m
1 /*
2  * Copyright (C) 2005 Apple Computer, 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 <WebKit/WebDefaultContextMenuDelegate.h>
30
31 #import <WebKit/WebAssertions.h>
32 #import <WebKit/DOM.h>
33 #import <WebKit/WebBridge.h>
34 #import <WebKit/WebDataSourcePrivate.h>
35 #import <WebKit/WebDefaultUIDelegate.h>
36 #import <WebKit/WebDOMOperations.h>
37 #import <WebKit/WebFramePrivate.h>
38 #import <WebKit/WebHTMLViewPrivate.h>
39 #import <WebKit/WebLocalizableStrings.h>
40 #import <WebKit/WebNSPasteboardExtras.h>
41 #import <WebKit/WebNSURLRequestExtras.h>
42 #import <WebKit/WebFrameView.h>
43 #import <WebKit/WebPolicyDelegate.h>
44 #import <WebKit/WebViewPrivate.h>
45 #import <WebKit/WebUIDelegate.h>
46 #import <WebKit/WebUIDelegatePrivate.h>
47
48 #import <WebCore/WebCoreBridge.h>
49
50 #import <Foundation/NSURLConnection.h>
51 #import <Foundation/NSURLRequest.h>
52
53 @implementation WebDefaultUIDelegate (WebContextMenu)
54
55 #ifndef OMIT_TIGER_FEATURES
56 static NSString *localizedMenuTitleFromAppKit(NSString *key, NSString *comment)
57 {
58     NSBundle *appKitBundle = [NSBundle bundleWithIdentifier:@"com.apple.AppKit"];
59     if (!appKitBundle) {
60         return key;
61     }
62     NSString *result = NSLocalizedStringFromTableInBundle(key, @"MenuCommands", appKitBundle, comment);
63     if (result == nil) {
64         return key;
65     }
66     return result;
67 }
68 #endif
69
70 - (NSMenuItem *)menuItemWithTag:(int)tag
71 {
72     NSMenuItem *menuItem = [[[NSMenuItem alloc] init] autorelease];
73     [menuItem setTag:tag];
74     
75     NSString *title = nil;
76     SEL action = NULL;
77     
78     switch(tag) {
79         case WebMenuItemTagOpenLinkInNewWindow:
80             title = UI_STRING("Open Link in New Window", "Open in New Window context menu item");
81             action = @selector(openLinkInNewWindow:);
82             [menuItem setTarget:self];
83             break;
84         case WebMenuItemTagDownloadLinkToDisk:
85             title = UI_STRING("Download Linked File", "Download Linked File context menu item");
86             action = @selector(downloadLinkToDisk:);
87             [menuItem setTarget:self];
88             break;
89         case WebMenuItemTagCopyLinkToClipboard:
90             title = UI_STRING("Copy Link", "Copy Link context menu item");
91             action = @selector(copyLinkToClipboard:);
92             [menuItem setTarget:self];
93             break;
94         case WebMenuItemTagOpenImageInNewWindow:
95             title = UI_STRING("Open Image in New Window", "Open Image in New Window context menu item");
96             action = @selector(openImageInNewWindow:);
97             [menuItem setTarget:self];
98             break;
99         case WebMenuItemTagDownloadImageToDisk:
100             title = UI_STRING("Download Image", "Download Image context menu item");
101             action = @selector(downloadImageToDisk:);
102             [menuItem setTarget:self];
103             break;
104         case WebMenuItemTagCopyImageToClipboard:
105             title = UI_STRING("Copy Image", "Copy Image context menu item");
106             action = @selector(copyImageToClipboard:);
107             [menuItem setTarget:self];
108             break;
109         case WebMenuItemTagOpenFrameInNewWindow:
110             title = UI_STRING("Open Frame in New Window", "Open Frame in New Window context menu item");
111             action = @selector(openFrameInNewWindow:);
112             [menuItem setTarget:self];
113             break;
114         case WebMenuItemTagCopy:
115             title = UI_STRING("Copy", "Copy context menu item");
116             action = @selector(copy:);
117             break;
118         case WebMenuItemTagGoBack:
119             title = UI_STRING("Back", "Back context menu item");
120             action = @selector(goBack:);
121             break;
122         case WebMenuItemTagGoForward:
123             title = UI_STRING("Forward", "Forward context menu item");
124             action = @selector(goForward:);
125             break;
126         case WebMenuItemTagStop:
127             title = UI_STRING("Stop", "Stop context menu item");
128             action = @selector(stopLoading:);
129             break;
130         case WebMenuItemTagReload:
131             title = UI_STRING("Reload", "Reload context menu item");
132             action = @selector(reload:);
133             break;
134         case WebMenuItemTagCut:
135             title = UI_STRING("Cut", "Cut context menu item");
136             action = @selector(cut:);
137             break;
138         case WebMenuItemTagPaste:
139             title = UI_STRING("Paste", "Paste context menu item");
140             action = @selector(paste:);
141             break;
142         case WebMenuItemTagSpellingGuess:
143             action = @selector(_changeSpellingFromMenu:);
144             break;
145         case WebMenuItemTagNoGuessesFound:
146             title = UI_STRING("No Guesses Found", "No Guesses Found context menu item");
147             break;
148         case WebMenuItemTagIgnoreSpelling:
149             title = UI_STRING("Ignore Spelling", "Ignore Spelling context menu item");
150             action = @selector(_ignoreSpellingFromMenu:);
151             break;
152         case WebMenuItemTagLearnSpelling:
153             title = UI_STRING("Learn Spelling", "Learn Spelling context menu item");
154             action = @selector(_learnSpellingFromMenu:);
155             break;
156 #ifndef OMIT_TIGER_FEATURES
157         case WebMenuItemTagSearchInSpotlight:
158             // FIXME: Perhaps move this string into WebKit directly when we're not in localization freeze
159             title = localizedMenuTitleFromAppKit(@"Search in Spotlight", @"Search in Spotlight menu title.");
160             action = @selector(_searchWithSpotlightFromMenu:);
161             break;
162         case WebMenuItemTagSearchInGoogle:
163             // FIXME: Perhaps move this string into WebKit directly when we're not in localization freeze
164             title = localizedMenuTitleFromAppKit(@"Search in Google", @"Search in Google menu title.");
165             action = @selector(_searchWithGoogleFromMenu:);
166             break;
167         case WebMenuItemTagLookUpInDictionary:
168             // FIXME: Perhaps move this string into WebKit directly when we're not in localization freeze
169             title = localizedMenuTitleFromAppKit(@"Look Up in Dictionary", @"Look Up in Dictionary menu title.");
170             action = @selector(_lookUpInDictionaryFromMenu:);
171             break;
172 #endif
173         default:
174             return nil;
175     }
176
177     if (title != nil) {
178         [menuItem setTitle:title];
179     }
180     [menuItem setAction:action];
181     
182     return menuItem;
183 }
184
185 - (NSArray *)contextMenuItemsForElement:(NSDictionary *)element
186 {
187     NSMutableArray *menuItems = [NSMutableArray array];
188     
189     NSURL *linkURL = [element objectForKey:WebElementLinkURLKey];
190     
191     if (linkURL) {
192         if([WebView _canHandleRequest:[NSURLRequest requestWithURL:linkURL]]) {
193             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagOpenLinkInNewWindow]];
194             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagDownloadLinkToDisk]];
195         }
196         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagCopyLinkToClipboard]];
197     }
198     
199     WebFrame *webFrame = [element objectForKey:WebElementFrameKey];
200     NSURL *imageURL = [element objectForKey:WebElementImageURLKey];
201     
202     if (imageURL) {
203         if (linkURL) {
204             [menuItems addObject:[NSMenuItem separatorItem]];
205         }
206         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagOpenImageInNewWindow]];
207         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagDownloadImageToDisk]];
208         if ([imageURL isFileURL] || [[webFrame dataSource] _fileWrapperForURL:imageURL]) {
209             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagCopyImageToClipboard]];
210         }
211     }
212     
213     if (!imageURL && !linkURL) {
214         if ([[element objectForKey:WebElementIsSelectedKey] boolValue]) {
215 #ifndef OMIT_TIGER_FEATURES
216             // Add Tiger-only items that act on selected text. Google search needn't be Tiger-only technically,
217             // but it's a new Tiger-only feature to have it in the context menu by default.
218             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagSearchInSpotlight]];
219             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagSearchInGoogle]];
220             [menuItems addObject:[NSMenuItem separatorItem]];
221             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagLookUpInDictionary]];
222             [menuItems addObject:[NSMenuItem separatorItem]];
223 #endif
224             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagCopy]];
225         } else {
226             WebView *wv = [webFrame webView];
227             if ([wv canGoBack]) {
228                 [menuItems addObject:[self menuItemWithTag:WebMenuItemTagGoBack]];
229             }
230             if ([wv canGoForward]) {
231                 [menuItems addObject:[self menuItemWithTag:WebMenuItemTagGoForward]];
232             }
233             if ([wv isLoading]) {
234                 [menuItems addObject:[self menuItemWithTag:WebMenuItemTagStop]];
235             } else {
236                 [menuItems addObject:[self menuItemWithTag:WebMenuItemTagReload]];
237             }
238             
239             if (webFrame != [wv mainFrame]) {
240                 [menuItems addObject:[self menuItemWithTag:WebMenuItemTagOpenFrameInNewWindow]];
241             }
242         }
243     }
244     
245     // Attach element as the represented object to each item.
246     [menuItems makeObjectsPerformSelector:@selector(setRepresentedObject:) withObject:element];
247     
248     return menuItems;
249 }
250
251 - (NSArray *)editingContextMenuItemsForElement:(NSDictionary *)element
252 {
253     NSMenuItem *item;
254     NSMutableArray *menuItems = [NSMutableArray array];
255     WebHTMLView *HTMLView = (WebHTMLView *)[[[element objectForKey:WebElementFrameKey] frameView] documentView];
256     ASSERT([HTMLView isKindOfClass:[WebHTMLView class]]);
257     
258     // Add spelling related context menu items.
259     if ([HTMLView _isSelectionMisspelled]) {
260         NSArray *guesses = [HTMLView _guessesForMisspelledSelection];
261         unsigned count = [guesses count];
262         if (count > 0) {
263             NSEnumerator *enumerator = [guesses objectEnumerator];
264             NSString *guess;
265             while ((guess = [enumerator nextObject]) != nil) {
266                 item = [self menuItemWithTag:WebMenuItemTagSpellingGuess];
267                 [item setTitle:guess];
268                 [menuItems addObject:item];
269             }
270         } else {
271             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagNoGuessesFound]];
272         }
273         [menuItems addObject:[NSMenuItem separatorItem]];
274         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagIgnoreSpelling]];
275         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagLearnSpelling]];
276         [menuItems addObject:[NSMenuItem separatorItem]];
277     }
278     
279 #ifndef OMIT_TIGER_FEATURES
280     // Add Tiger-only items that aren't in our nib.
281     // FIXME: When we're not building for Panther anymore we should update the nib to include these.
282     [menuItems addObject:[self menuItemWithTag:WebMenuItemTagSearchInSpotlight]];
283     [menuItems addObject:[self menuItemWithTag:WebMenuItemTagSearchInGoogle]];
284     [menuItems addObject:[NSMenuItem separatorItem]];
285     // FIXME: The NSTextView behavior for looking text up in the dictionary is different if
286     // there was a selection before you clicked than if the selection was created as part of
287     // the click. This is desired by the dictionary folks apparently, though it seems bizarre.
288     // It might be tricky to pull this off in WebKit.
289     [menuItems addObject:[self menuItemWithTag:WebMenuItemTagLookUpInDictionary]];
290     [menuItems addObject:[NSMenuItem separatorItem]];
291 #endif
292     
293     // Load our NSTextView-like context menu nib.
294     if (defaultMenu == nil) {
295         static NSNib *textViewMenuNib = nil;
296         if (textViewMenuNib == nil) {
297             textViewMenuNib = [[NSNib alloc] initWithNibNamed:@"WebViewEditingContextMenu" bundle:[NSBundle bundleForClass:[self class]]];
298         }
299         [textViewMenuNib instantiateNibWithOwner:self topLevelObjects:nil];
300     }
301     
302     // Add tags to the menu items because this is what the WebUIDelegate expects.
303     NSEnumerator *enumerator = [[defaultMenu itemArray] objectEnumerator];
304     while ((item = [enumerator nextObject]) != nil) {
305         item = [item copy];
306         SEL action = [item action];
307         int tag;
308         if (action == @selector(cut:)) {
309             tag = WebMenuItemTagCut;
310         } else if (action == @selector(copy:)) {
311             tag = WebMenuItemTagCopy;
312         } else if (action == @selector(paste:)) {
313             tag = WebMenuItemTagPaste;
314         } else {
315             tag = WebMenuItemTagOther;
316         }
317         [item setTag:tag];
318         [menuItems addObject:item];
319         [item release];
320     }
321     
322     return menuItems;
323 }
324
325 - (NSArray *)webView:(WebView *)wv contextMenuItemsForElement:(NSDictionary *)element  defaultMenuItems:(NSArray *)defaultMenuItems
326 {
327     WebHTMLView *HTMLView = (WebHTMLView *)[[[element objectForKey:WebElementFrameKey] frameView] documentView];
328     if ([HTMLView isKindOfClass:[WebHTMLView class]] && [HTMLView _isEditable]) {
329         return [self editingContextMenuItemsForElement:element];
330     } else {
331         return [self contextMenuItemsForElement:element];
332     }
333 }
334
335 - (void)openNewWindowWithURL:(NSURL *)URL element:(NSDictionary *)element
336 {
337     WebFrame *webFrame = [element objectForKey:WebElementFrameKey];
338     WebView *webView = [webFrame webView];
339     
340     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
341     NSString *referrer = [[webFrame _bridge] referrer];
342     if (referrer) {
343         [request _web_setHTTPReferrer:referrer];
344     }
345     
346     [webView _openNewWindowWithRequest:request];
347 }
348
349 - (void)downloadURL:(NSURL *)URL element:(NSDictionary *)element
350 {
351     WebFrame *webFrame = [element objectForKey:WebElementFrameKey];
352     WebView *webView = [webFrame webView];
353     [webView _downloadURL:URL];
354 }
355
356 - (void)openLinkInNewWindow:(id)sender
357 {
358     NSDictionary *element = [sender representedObject];
359     [self openNewWindowWithURL:[element objectForKey:WebElementLinkURLKey] element:element];
360 }
361
362 - (void)downloadLinkToDisk:(id)sender
363 {
364     NSDictionary *element = [sender representedObject];
365     [self downloadURL:[element objectForKey:WebElementLinkURLKey] element:element];
366 }
367
368 - (void)copyLinkToClipboard:(id)sender
369 {
370     NSDictionary *element = [sender representedObject];
371     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
372     NSArray *types = [NSPasteboard _web_writableTypesForURL];
373     [pasteboard declareTypes:types owner:self];    
374     [[[element objectForKey:WebElementFrameKey] webView] _writeLinkElement:element 
375                                                        withPasteboardTypes:types
376                                                               toPasteboard:pasteboard];
377 }
378
379 - (void)openImageInNewWindow:(id)sender
380 {
381     NSDictionary *element = [sender representedObject];
382     [self openNewWindowWithURL:[element objectForKey:WebElementImageURLKey] element:element];
383 }
384
385 - (void)downloadImageToDisk:(id)sender
386 {
387     NSDictionary *element = [sender representedObject];
388     [self downloadURL:[element objectForKey:WebElementImageURLKey] element:element];
389 }
390
391 - (void)copyImageToClipboard:(id)sender
392 {
393     NSDictionary *element = [sender representedObject];
394     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
395     NSArray *types = [NSPasteboard _web_writableTypesForImageIncludingArchive:([element objectForKey:WebElementDOMNodeKey] != nil)];
396     [pasteboard declareTypes:types owner:self];
397     [[[element objectForKey:WebElementFrameKey] webView] _writeImageElement:element 
398                                                         withPasteboardTypes:types 
399                                                                toPasteboard:pasteboard];
400 }
401
402 - (void)openFrameInNewWindow:(id)sender
403 {
404     NSDictionary *element = [sender representedObject];
405     WebDataSource *dataSource = [[element objectForKey:WebElementFrameKey] dataSource];
406     NSURL *URL = [dataSource unreachableURL];
407     if (URL == nil) {
408         URL = [[dataSource request] URL];
409     }    
410     [self openNewWindowWithURL:URL element:element];
411 }
412
413 @end