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