WebCore:
[WebKit-https.git] / WebKit / DefaultDelegates / 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 "WebDefaultContextMenuDelegate.h"
30
31 #import "WebDOMOperations.h"
32 #import "WebDataSourcePrivate.h"
33 #import "WebDefaultUIDelegate.h"
34 #import "WebFrameBridge.h"
35 #import "WebFrameInternal.h"
36 #import "WebFrameView.h"
37 #import "WebHTMLViewPrivate.h"
38 #import "WebLocalizableStrings.h"
39 #import "WebNSPasteboardExtras.h"
40 #import "WebNSURLRequestExtras.h"
41 #import "WebPolicyDelegate.h"
42 #import "WebUIDelegate.h"
43 #import "WebUIDelegatePrivate.h"
44 #import "WebViewInternal.h"
45 #import <Foundation/NSURLConnection.h>
46 #import <Foundation/NSURLRequest.h>
47 #import <JavaScriptCore/Assertions.h>
48 #import <WebCore/FrameLoader.h>
49 #import <WebCore/FrameMac.h>
50 #import <WebCore/WebCoreFrameBridge.h>
51 #import <WebKit/DOM.h>
52 #import <WebKit/DOMPrivate.h>
53
54 @implementation WebDefaultUIDelegate (WebContextMenu)
55
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
69 - (NSMenuItem *)menuItemWithTag:(int)tag target:(id)target representedObject:(id)representedObject
70 {
71     NSMenuItem *menuItem = [[[NSMenuItem alloc] init] autorelease];
72     [menuItem setTag:tag];
73     [menuItem setTarget:target]; // can be nil
74     [menuItem setRepresentedObject:representedObject];
75     
76     NSString *title = nil;
77     SEL action = NULL;
78     
79     switch(tag) {
80         case WebMenuItemTagOpenLink:
81             title = UI_STRING("Open Link", "Open Link context menu item");
82             action = @selector(openLink:);
83             break;
84         case WebMenuItemTagOpenLinkInNewWindow:
85             title = UI_STRING("Open Link in New Window", "Open in New Window context menu item");
86             action = @selector(openLinkInNewWindow:);
87             break;
88         case WebMenuItemTagDownloadLinkToDisk:
89             title = UI_STRING("Download Linked File", "Download Linked File context menu item");
90             action = @selector(downloadLinkToDisk:);
91             break;
92         case WebMenuItemTagCopyLinkToClipboard:
93             title = UI_STRING("Copy Link", "Copy Link context menu item");
94             action = @selector(copyLinkToClipboard:);
95             break;
96         case WebMenuItemTagOpenImageInNewWindow:
97             title = UI_STRING("Open Image in New Window", "Open Image in New Window context menu item");
98             action = @selector(openImageInNewWindow:);
99             break;
100         case WebMenuItemTagDownloadImageToDisk:
101             title = UI_STRING("Download Image", "Download Image context menu item");
102             action = @selector(downloadImageToDisk:);
103             break;
104         case WebMenuItemTagCopyImageToClipboard:
105             title = UI_STRING("Copy Image", "Copy Image context menu item");
106             action = @selector(copyImageToClipboard:);
107             break;
108         case WebMenuItemTagOpenFrameInNewWindow:
109             title = UI_STRING("Open Frame in New Window", "Open Frame in New Window context menu item");
110             action = @selector(openFrameInNewWindow:);
111             break;
112         case WebMenuItemTagCopy:
113             title = UI_STRING("Copy", "Copy context menu item");
114             action = @selector(copy:);
115             break;
116         case WebMenuItemTagGoBack:
117             title = UI_STRING("Back", "Back context menu item");
118             action = @selector(goBack:);
119             break;
120         case WebMenuItemTagGoForward:
121             title = UI_STRING("Forward", "Forward context menu item");
122             action = @selector(goForward:);
123             break;
124         case WebMenuItemTagStop:
125             title = UI_STRING("Stop", "Stop context menu item");
126             action = @selector(stopLoading:);
127             break;
128         case WebMenuItemTagReload:
129             title = UI_STRING("Reload", "Reload context menu item");
130             action = @selector(reload:);
131             break;
132         case WebMenuItemTagCut:
133             title = UI_STRING("Cut", "Cut context menu item");
134             action = @selector(cut:);
135             break;
136         case WebMenuItemTagPaste:
137             title = UI_STRING("Paste", "Paste context menu item");
138             action = @selector(paste:);
139             break;
140         case WebMenuItemTagSpellingGuess:
141             action = @selector(_changeSpellingFromMenu:);
142             break;
143         case WebMenuItemTagNoGuessesFound:
144             title = UI_STRING("No Guesses Found", "No Guesses Found context menu item");
145             break;
146         case WebMenuItemTagIgnoreSpelling:
147             title = UI_STRING("Ignore Spelling", "Ignore Spelling context menu item");
148             action = @selector(_ignoreSpellingFromMenu:);
149             break;
150 #ifndef BUILDING_ON_TIGER
151         case WebMenuItemTagIgnoreGrammar:
152             title = UI_STRING("Ignore Grammar", "Ignore Grammar context menu item");
153             action = @selector(_ignoreGrammarFromMenu:);
154             break;
155 #endif
156         case WebMenuItemTagLearnSpelling:
157             title = UI_STRING("Learn Spelling", "Learn Spelling context menu item");
158             action = @selector(_learnSpellingFromMenu:);
159             break;
160         case WebMenuItemTagSearchInSpotlight:
161             // FIXME: Perhaps move this string into WebKit directly when we're not in localization freeze
162             title = localizedMenuTitleFromAppKit(@"Search in Spotlight", @"Search in Spotlight menu title.");
163             action = @selector(_searchWithSpotlightFromMenu:);
164             break;
165         case WebMenuItemTagSearchWeb:
166             // FIXME: Perhaps move this string into WebKit directly when we're not in localization freeze
167             title = localizedMenuTitleFromAppKit(@"Search in Google", @"Search in Google menu title.");
168             action = @selector(_searchWithGoogleFromMenu:);
169             break;
170         case WebMenuItemTagLookUpInDictionary:
171             // FIXME: Perhaps move this string into WebKit directly when we're not in localization freeze
172             title = localizedMenuTitleFromAppKit(@"Look Up in Dictionary", @"Look Up in Dictionary menu title.");
173             action = @selector(_lookUpInDictionaryFromMenu:);
174             break;
175         default:
176             return nil;
177     }
178
179     if (title != nil) {
180         [menuItem setTitle:title];
181     }
182     [menuItem setAction:action];
183     
184     return menuItem;
185 }
186
187 - (void)appendDefaultItems:(NSArray *)defaultItems toArray:(NSMutableArray *)menuItems
188 {
189     ASSERT_ARG(menuItems, menuItems != nil);
190     if ([defaultItems count] > 0) {
191         ASSERT(![[menuItems lastObject] isSeparatorItem]);
192         if (![[defaultItems objectAtIndex:0] isSeparatorItem]) {
193             [menuItems addObject:[NSMenuItem separatorItem]];
194             
195             NSEnumerator *e = [defaultItems objectEnumerator];
196             NSMenuItem *item;
197             while ((item = [e nextObject]) != nil) {
198                 [menuItems addObject:item];
199             }
200         }
201     }
202 }
203
204 - (NSArray *)contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems
205 {
206     // The defaultMenuItems here are ones supplied by the WebDocumentView protocol implementation. WebPDFView is
207     // one case that has non-nil default items here.
208     NSMutableArray *menuItems = [NSMutableArray array];
209     
210     NSURL *linkURL = [element objectForKey:WebElementLinkURLKey];
211     
212     if (linkURL) {
213         if([WebView _canHandleRequest:[NSURLRequest requestWithURL:linkURL]]) {
214             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagOpenLinkInNewWindow target:self representedObject:element]];
215             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagDownloadLinkToDisk target:self representedObject:element]];
216         }
217         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagCopyLinkToClipboard target:self representedObject:element]];
218     }
219
220     WebFrame *webFrame = [element objectForKey:WebElementFrameKey];
221     NSURL *imageURL = [element objectForKey:WebElementImageURLKey];
222     
223     if (imageURL) {
224         if (linkURL) {
225             [menuItems addObject:[NSMenuItem separatorItem]];
226         }
227         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagOpenImageInNewWindow target:self representedObject:element]];
228         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagDownloadImageToDisk target:self representedObject:element]];
229         if ([imageURL isFileURL] || [[webFrame dataSource] _fileWrapperForURL:imageURL]) {
230             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagCopyImageToClipboard target:self representedObject:element]];
231         }
232     }
233     
234     if (!imageURL && !linkURL) {
235         if ([[element objectForKey:WebElementIsSelectedKey] boolValue]) {
236             // The Spotlight and Google items are implemented in WebView, and require that the
237             // current document view conforms to WebDocumentText
238             ASSERT([[[webFrame frameView] documentView] conformsToProtocol:@protocol(WebDocumentText)]);
239             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagSearchInSpotlight target:nil representedObject:element]];
240             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagSearchWeb target:nil representedObject:element]];
241             [menuItems addObject:[NSMenuItem separatorItem]];
242
243             // FIXME 4184640: The Look Up in Dictionary item is only implemented in WebHTMLView, and so is present but
244             // dimmed for other cases where WebElementIsSelectedKey is present. It would probably 
245             // be better not to include it in the menu if the documentView isn't a WebHTMLView, but that could break 
246             // existing clients that have code that relies on it being present (unlikely for clients outside of Apple, 
247             // but Safari has such code).
248             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagLookUpInDictionary target:nil representedObject:element]];            
249             [menuItems addObject:[NSMenuItem separatorItem]];
250             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagCopy target:nil representedObject:element]];
251         } else {
252             WebView *wv = [webFrame webView];
253             if ([wv canGoBack]) {
254                 [menuItems addObject:[self menuItemWithTag:WebMenuItemTagGoBack target:wv representedObject:element]];
255             }
256             if ([wv canGoForward]) {
257                 [menuItems addObject:[self menuItemWithTag:WebMenuItemTagGoForward target:wv representedObject:element]];
258             }
259             if ([wv isLoading]) {
260                 [menuItems addObject:[self menuItemWithTag:WebMenuItemTagStop target:wv representedObject:element]];
261             } else {
262                 [menuItems addObject:[self menuItemWithTag:WebMenuItemTagReload target:wv representedObject:element]];
263             }
264             
265             if (webFrame != [wv mainFrame]) {
266                 [menuItems addObject:[self menuItemWithTag:WebMenuItemTagOpenFrameInNewWindow target:self representedObject:element]];
267             }
268         }
269     }
270     
271     // Add the default items at the end, if any, after a separator
272     [self appendDefaultItems:defaultMenuItems toArray:menuItems];
273
274     return menuItems;
275 }
276
277 - (NSArray *)editingContextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems
278 {
279     NSMutableArray *menuItems = [NSMutableArray array];
280     NSMenuItem *item;
281     WebFrame *webFrame = [element objectForKey:WebElementFrameKey];
282     WebHTMLView *HTMLView = (WebHTMLView *)[[webFrame frameView] documentView];
283     ASSERT([HTMLView isKindOfClass:[WebHTMLView class]]);
284     BOOL inPasswordField = [HTMLView _isSelectionInPasswordField];
285
286     if (!inPasswordField) {
287         // Consider adding spelling-related or grammar-related context menu items (never both)
288         if ([HTMLView _isSelectionMisspelled]) {
289             NSArray *guesses = [HTMLView _guessesForMisspelledSelection];
290             unsigned count = [guesses count];
291             if (count > 0) {
292                 NSEnumerator *enumerator = [guesses objectEnumerator];
293                 NSString *guess;
294                 while ((guess = [enumerator nextObject]) != nil) {
295                     item = [self menuItemWithTag:WebMenuItemTagSpellingGuess target:nil representedObject:element];
296                     [item setTitle:guess];
297                     [menuItems addObject:item];
298                 }
299             } else {
300                 [menuItems addObject:[self menuItemWithTag:WebMenuItemTagNoGuessesFound target:nil representedObject:element]];
301             }
302             [menuItems addObject:[NSMenuItem separatorItem]];
303             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagIgnoreSpelling target:nil representedObject:element]];
304             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagLearnSpelling target:nil representedObject:element]];
305             [menuItems addObject:[NSMenuItem separatorItem]];
306         } else if ([[webFrame webView] isGrammarCheckingEnabled] && [HTMLView _isSelectionUngrammatical]) {
307 #ifdef BUILDING_ON_TIGER
308             ASSERT_NOT_REACHED();
309 #else
310             // These strings are being converted from NSString to WebCore::String and back again, which
311             // would offend our sensibilities something awful except that we're moving all the context menu code
312             // to WebCore soon where we won't have to do this.
313             Vector<WebCore::String> guesses = core(webFrame)->guessesForUngrammaticalSelection();
314             size_t count = guesses.size();
315             
316             // If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions
317             // list and trailing separator rather than adding a "No Guesses Found" item (matches AppKit)
318             if (count > 0) {
319                 for (unsigned i = 0; i < count; ++i) {
320                     NSString *string = guesses.at(i);
321                     item = [self menuItemWithTag:WebMenuItemTagSpellingGuess target:nil representedObject:element];
322                     [item setTitle:string];
323                     [menuItems addObject:item];
324                 }
325                 [menuItems addObject:[NSMenuItem separatorItem]];
326             }
327             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagIgnoreGrammar target:nil representedObject:element]];
328             [menuItems addObject:[NSMenuItem separatorItem]];
329 #endif
330         }
331     }
332
333     NSURL *linkURL = [element objectForKey:WebElementLinkURLKey];
334     
335     if ([[element objectForKey:WebElementIsSelectedKey] boolValue] && !inPasswordField) {
336         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagSearchInSpotlight target:nil representedObject:element]];
337         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagSearchWeb target:nil representedObject:element]];
338         [menuItems addObject:[NSMenuItem separatorItem]];
339
340         // FIXME: The NSTextView behavior for looking text up in the dictionary is different if
341         // there was a selection before you clicked than if the selection was created as part of
342         // the click. This is desired by the dictionary folks apparently, though it seems bizarre.
343         // It might be tricky to pull this off in WebKit.
344         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagLookUpInDictionary target:nil representedObject:element]];
345         [menuItems addObject:[NSMenuItem separatorItem]];
346     }
347
348     if (linkURL) {
349         if([WebView _canHandleRequest:[NSURLRequest requestWithURL:linkURL]]) {
350             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagOpenLink target:self representedObject:element]];
351             [menuItems addObject:[self menuItemWithTag:WebMenuItemTagOpenLinkInNewWindow target:self representedObject:element]];
352         }
353         [menuItems addObject:[self menuItemWithTag:WebMenuItemTagCopyLinkToClipboard target:self representedObject:element]];
354         // FIXME: we should add "Edit Link..." here like NSTextView has in it's context menu
355         [menuItems addObject:[NSMenuItem separatorItem]];
356     }
357
358     // Load our NSTextView-like context menu nib.
359     if (defaultMenu == nil) {
360         static NSNib *textViewMenuNib = nil;
361         if (!textViewMenuNib) {
362 #if BUILDING_ON_TIGER
363             NSString *contextMenuNibName = @"WebViewEditingContextMenuOld";
364 #else
365             NSString *contextMenuNibName = @"WebViewEditingContextMenu";
366 #endif
367             textViewMenuNib = [[NSNib alloc] initWithNibNamed:contextMenuNibName bundle:[NSBundle bundleForClass:[self class]]];
368         }
369         [textViewMenuNib instantiateNibWithOwner:self topLevelObjects:nil];
370         ASSERT(defaultMenu != nil);
371     }
372     
373     // Add tags to the menu items because this is what the WebUIDelegate expects.
374     // Also remove any future-looking items if appropriate.
375     NSEnumerator *enumerator = [[defaultMenu itemArray] objectEnumerator];
376     while ((item = [enumerator nextObject]) != nil) {
377         item = [item copy];
378         SEL action = [item action];
379         int tag;
380         bool validForPassword = true;
381         if (action == @selector(cut:)) {
382             tag = WebMenuItemTagCut;
383         } else if (action == @selector(copy:)) {
384             tag = WebMenuItemTagCopy;
385         } else if (action == @selector(paste:)) {
386             tag = WebMenuItemTagPaste;
387         } else {
388             // FIXME 4158153: we should supply tags for each known item so clients can make
389             // sensible decisions, like we do with PDF context menu items (see WebPDFView.mm).
390             // FIXME: This ignores the contents of submenus entirely.
391
392             // Once we have other tag names, we should reconsider if any of them are valid for password fields.
393             tag = WebMenuItemTagOther;
394             validForPassword = false;
395         }
396         
397         if (!inPasswordField || validForPassword) {
398             [item setTag:tag];
399             [menuItems addObject:item];
400         }
401         [item release];
402     }
403     
404     // Add the default items at the end, if any, after a separator
405     [self appendDefaultItems:defaultMenuItems toArray:menuItems];
406         
407     return menuItems;
408 }
409
410 - (NSArray *)webView:(WebView *)wv contextMenuItemsForElement:(NSDictionary *)element  defaultMenuItems:(NSArray *)defaultMenuItems
411 {
412     BOOL contentEditible = [wv isEditable] || [[element objectForKey:WebElementIsContentEditableKey] boolValue];
413     NSView *documentView = [[[element objectForKey:WebElementFrameKey] frameView] documentView];
414     if ([documentView isKindOfClass:[WebHTMLView class]] && contentEditible) {
415         return [self editingContextMenuItemsForElement:element defaultMenuItems:defaultMenuItems];
416     } else {
417         return [self contextMenuItemsForElement:element defaultMenuItems:defaultMenuItems];
418     }
419 }
420
421 - (NSURLRequest *)requestWithURL:(NSURL *)URL includingReferrerFromFrame:(WebFrame *)webFrame
422 {
423     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
424     NSString *referrer = core(webFrame)->loader()->outgoingReferrer();
425     if (referrer)
426         [request _web_setHTTPReferrer:referrer];
427     return request;
428 }
429
430 - (void)openNewWindowWithURL:(NSURL *)URL element:(NSDictionary *)element
431 {
432     WebFrame *webFrame = [element objectForKey:WebElementFrameKey];
433     WebView *webView = [webFrame webView];
434     [webView _openNewWindowWithRequest:[self requestWithURL:URL includingReferrerFromFrame:webFrame]];
435 }
436
437 - (void)downloadURL:(NSURL *)URL element:(NSDictionary *)element
438 {
439     WebFrame *webFrame = [element objectForKey:WebElementFrameKey];
440     WebView *webView = [webFrame webView];
441     [webView _downloadURL:URL];
442 }
443
444 - (void)openLink:(id)sender
445 {
446     NSDictionary *element = [sender representedObject];
447     WebFrame *webFrame = [element objectForKey:WebElementFrameKey];
448     WebFrame *targetFrame = [element objectForKey:WebElementLinkTargetFrameKey];
449     NSURL *url = [element objectForKey:WebElementLinkURLKey];
450     if (targetFrame)
451         [targetFrame loadRequest:[self requestWithURL:url includingReferrerFromFrame:webFrame]];
452     else
453         [self openNewWindowWithURL:url element:element];
454 }
455
456 - (void)openLinkInNewWindow:(id)sender
457 {
458     NSDictionary *element = [sender representedObject];
459     [self openNewWindowWithURL:[element objectForKey:WebElementLinkURLKey] element:element];
460 }
461
462 - (void)downloadLinkToDisk:(id)sender
463 {
464     NSDictionary *element = [sender representedObject];
465     [self downloadURL:[element objectForKey:WebElementLinkURLKey] element:element];
466 }
467
468 - (void)copyLinkToClipboard:(id)sender
469 {
470     NSDictionary *element = [sender representedObject];
471     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
472     NSArray *types = [NSPasteboard _web_writableTypesForURL];
473     [pasteboard declareTypes:types owner:self];    
474     [[[element objectForKey:WebElementFrameKey] webView] _writeLinkElement:element 
475                                                        withPasteboardTypes:types
476                                                               toPasteboard:pasteboard];
477 }
478
479 - (void)openImageInNewWindow:(id)sender
480 {
481     NSDictionary *element = [sender representedObject];
482     [self openNewWindowWithURL:[element objectForKey:WebElementImageURLKey] element:element];
483 }
484
485 - (void)downloadImageToDisk:(id)sender
486 {
487     NSDictionary *element = [sender representedObject];
488     [self downloadURL:[element objectForKey:WebElementImageURLKey] element:element];
489 }
490
491 - (void)copyImageToClipboard:(id)sender
492 {
493     NSDictionary *element = [sender representedObject];
494     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
495     NSArray *types = [NSPasteboard _web_writableTypesForImageIncludingArchive:([element objectForKey:WebElementDOMNodeKey] != nil)];
496     [pasteboard declareTypes:types owner:self];
497     [[[element objectForKey:WebElementFrameKey] webView] _writeImageForElement:element 
498                                                         withPasteboardTypes:types 
499                                                                toPasteboard:pasteboard];
500 }
501
502 - (void)openFrameInNewWindow:(id)sender
503 {
504     NSDictionary *element = [sender representedObject];
505     WebDataSource *dataSource = [[element objectForKey:WebElementFrameKey] dataSource];
506     NSURL *URL = [dataSource unreachableURL];
507     if (URL == nil) {
508         URL = [[dataSource request] URL];
509     }    
510     [self openNewWindowWithURL:URL element:element];
511 }
512
513 @end