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