cfccd3cd64926521976c6bf1856b6e8fb453dd33
[WebKit-https.git] / Source / WebKit / mac / WebCoreSupport / WebContextMenuClient.mm
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2015 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 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 #if !PLATFORM(IOS)
30
31 #import "WebContextMenuClient.h"
32
33 #import "WebDelegateImplementationCaching.h"
34 #import "WebElementDictionary.h"
35 #import "WebFrame.h"
36 #import "WebFrameInternal.h"
37 #import "WebHTMLView.h"
38 #import "WebHTMLViewInternal.h"
39 #import "WebKitVersionChecks.h"
40 #import "WebNSPasteboardExtras.h"
41 #import "WebSharingServicePickerController.h"
42 #import "WebUIDelegate.h"
43 #import "WebUIDelegatePrivate.h"
44 #import "WebView.h"
45 #import "WebViewInternal.h"
46 #import <WebCore/BitmapImage.h>
47 #import <WebCore/ContextMenu.h>
48 #import <WebCore/ContextMenuController.h>
49 #import <WebCore/Document.h>
50 #import <WebCore/Frame.h>
51 #import <WebCore/FrameView.h>
52 #import <WebCore/GraphicsContext.h>
53 #import <WebCore/ImageBuffer.h>
54 #import <WebCore/LocalizedStrings.h>
55 #import <WebCore/NSSharingServicePickerSPI.h>
56 #import <WebCore/Page.h>
57 #import <WebCore/RenderBox.h>
58 #import <WebCore/RenderObject.h>
59 #import <WebCore/SharedBuffer.h>
60 #import <WebCore/RuntimeApplicationChecks.h>
61 #import <WebCore/URL.h>
62 #import <WebKitLegacy/DOMPrivate.h>
63
64 using namespace WebCore;
65
66 @interface NSApplication (AppKitSecretsIKnowAbout)
67 - (void)speakString:(NSString *)string;
68 @end
69
70 WebContextMenuClient::WebContextMenuClient(WebView *webView)
71 #if ENABLE(SERVICE_CONTROLS)
72     : WebSharingServicePickerClient(webView)
73 #else
74     : m_webView(webView)
75 #endif
76 {
77 }
78
79 WebContextMenuClient::~WebContextMenuClient()
80 {
81 #if ENABLE(SERVICE_CONTROLS)
82     if (m_sharingServicePickerController)
83         [m_sharingServicePickerController clear];
84 #endif
85 }
86
87 void WebContextMenuClient::contextMenuDestroyed()
88 {
89     delete this;
90 }
91
92 static BOOL isPreVersion3Client(void)
93 {
94     static BOOL preVersion3Client = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_3_0_CONTEXT_MENU_TAGS);
95     return preVersion3Client;
96 }
97
98 static BOOL isPreInspectElementTagClient(void)
99 {
100     static BOOL preInspectElementTagClient = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_INSPECT_ELEMENT_MENU_TAG);
101     return preInspectElementTagClient;
102 }
103
104 static NSMutableArray *fixMenusToSendToOldClients(NSMutableArray *defaultMenuItems)
105 {
106     NSMutableArray *savedItems = nil;
107
108     unsigned defaultItemsCount = [defaultMenuItems count];
109
110     if (isPreInspectElementTagClient() && defaultItemsCount >= 2) {
111         NSMenuItem *secondToLastItem = [defaultMenuItems objectAtIndex:defaultItemsCount - 2];
112         NSMenuItem *lastItem = [defaultMenuItems objectAtIndex:defaultItemsCount - 1];
113
114         if ([secondToLastItem isSeparatorItem] && [lastItem tag] == WebMenuItemTagInspectElement) {
115             savedItems = [NSMutableArray arrayWithCapacity:2];
116             [savedItems addObject:secondToLastItem];
117             [savedItems addObject:lastItem];
118
119             [defaultMenuItems removeObject:secondToLastItem];
120             [defaultMenuItems removeObject:lastItem];
121             defaultItemsCount -= 2;
122         }
123     }
124
125     BOOL preVersion3Client = isPreVersion3Client();
126     if (!preVersion3Client)
127         return savedItems;
128         
129     for (unsigned i = 0; i < defaultItemsCount; ++i) {
130         NSMenuItem *item = [defaultMenuItems objectAtIndex:i];
131         int tag = [item tag];
132         int oldStyleTag = tag;
133
134         if (tag >= WEBMENUITEMTAG_WEBKIT_3_0_SPI_START) {
135             // Change all editing-related SPI tags listed in WebUIDelegatePrivate.h to WebMenuItemTagOther
136             // to match our old WebKit context menu behavior.
137             oldStyleTag = WebMenuItemTagOther;
138         } else {
139             // All items are expected to have useful tags coming into this method.
140             ASSERT(tag != WebMenuItemTagOther);
141             
142             // Use the pre-3.0 tags for the few items that changed tags as they moved from SPI to API. We
143             // do this only for old clients; new Mail already expects the new symbols in this case.
144             if (preVersion3Client) {
145                 switch (tag) {
146                     case WebMenuItemTagSearchInSpotlight:
147                         oldStyleTag = OldWebMenuItemTagSearchInSpotlight;
148                         break;
149                     case WebMenuItemTagSearchWeb:
150                         oldStyleTag = OldWebMenuItemTagSearchWeb;
151                         break;
152                     case WebMenuItemTagLookUpInDictionary:
153                         oldStyleTag = OldWebMenuItemTagLookUpInDictionary;
154                         break;
155                     default:
156                         break;
157                 }
158             }
159         }
160
161         if (oldStyleTag != tag)
162             [item setTag:oldStyleTag];
163     }
164
165     return savedItems;
166 }
167
168 static void fixMenusReceivedFromOldClients(NSMutableArray *newMenuItems, NSMutableArray *savedItems)
169 {   
170     if (savedItems)
171         [newMenuItems addObjectsFromArray:savedItems];
172
173     BOOL preVersion3Client = isPreVersion3Client();
174     if (!preVersion3Client)
175         return;
176     
177     // Restore the modern tags to the menu items whose tags we altered in fixMenusToSendToOldClients. 
178     unsigned newItemsCount = [newMenuItems count];
179     for (unsigned i = 0; i < newItemsCount; ++i) {
180         NSMenuItem *item = [newMenuItems objectAtIndex:i];
181         
182         int tag = [item tag];
183         int modernTag = tag;
184         
185         if (tag == WebMenuItemTagOther) {
186             // Restore the specific tag for items on which we temporarily set WebMenuItemTagOther to match old behavior.
187             NSString *title = [item title];
188             if ([title isEqualToString:contextMenuItemTagOpenLink()])
189                 modernTag = WebMenuItemTagOpenLink;
190             else if ([title isEqualToString:contextMenuItemTagIgnoreGrammar()])
191                 modernTag = WebMenuItemTagIgnoreGrammar;
192             else if ([title isEqualToString:contextMenuItemTagSpellingMenu()])
193                 modernTag = WebMenuItemTagSpellingMenu;
194             else if ([title isEqualToString:contextMenuItemTagShowSpellingPanel(true)]
195                      || [title isEqualToString:contextMenuItemTagShowSpellingPanel(false)])
196                 modernTag = WebMenuItemTagShowSpellingPanel;
197             else if ([title isEqualToString:contextMenuItemTagCheckSpelling()])
198                 modernTag = WebMenuItemTagCheckSpelling;
199             else if ([title isEqualToString:contextMenuItemTagCheckSpellingWhileTyping()])
200                 modernTag = WebMenuItemTagCheckSpellingWhileTyping;
201             else if ([title isEqualToString:contextMenuItemTagCheckGrammarWithSpelling()])
202                 modernTag = WebMenuItemTagCheckGrammarWithSpelling;
203             else if ([title isEqualToString:contextMenuItemTagFontMenu()])
204                 modernTag = WebMenuItemTagFontMenu;
205             else if ([title isEqualToString:contextMenuItemTagShowFonts()])
206                 modernTag = WebMenuItemTagShowFonts;
207             else if ([title isEqualToString:contextMenuItemTagBold()])
208                 modernTag = WebMenuItemTagBold;
209             else if ([title isEqualToString:contextMenuItemTagItalic()])
210                 modernTag = WebMenuItemTagItalic;
211             else if ([title isEqualToString:contextMenuItemTagUnderline()])
212                 modernTag = WebMenuItemTagUnderline;
213             else if ([title isEqualToString:contextMenuItemTagOutline()])
214                 modernTag = WebMenuItemTagOutline;
215             else if ([title isEqualToString:contextMenuItemTagStyles()])
216                 modernTag = WebMenuItemTagStyles;
217             else if ([title isEqualToString:contextMenuItemTagShowColors()])
218                 modernTag = WebMenuItemTagShowColors;
219             else if ([title isEqualToString:contextMenuItemTagSpeechMenu()])
220                 modernTag = WebMenuItemTagSpeechMenu;
221             else if ([title isEqualToString:contextMenuItemTagStartSpeaking()])
222                 modernTag = WebMenuItemTagStartSpeaking;
223             else if ([title isEqualToString:contextMenuItemTagStopSpeaking()])
224                 modernTag = WebMenuItemTagStopSpeaking;
225             else if ([title isEqualToString:contextMenuItemTagWritingDirectionMenu()])
226                 modernTag = WebMenuItemTagWritingDirectionMenu;
227             else if ([title isEqualToString:contextMenuItemTagDefaultDirection()])
228                 modernTag = WebMenuItemTagDefaultDirection;
229             else if ([title isEqualToString:contextMenuItemTagLeftToRight()])
230                 modernTag = WebMenuItemTagLeftToRight;
231             else if ([title isEqualToString:contextMenuItemTagRightToLeft()])
232                 modernTag = WebMenuItemTagRightToLeft;
233             else if ([title isEqualToString:contextMenuItemTagInspectElement()])
234                 modernTag = WebMenuItemTagInspectElement;
235             else if ([title isEqualToString:contextMenuItemTagCorrectSpellingAutomatically()])
236                 modernTag = WebMenuItemTagCorrectSpellingAutomatically;
237             else if ([title isEqualToString:contextMenuItemTagSubstitutionsMenu()])
238                 modernTag = WebMenuItemTagSubstitutionsMenu;
239             else if ([title isEqualToString:contextMenuItemTagShowSubstitutions(true)]
240                      || [title isEqualToString:contextMenuItemTagShowSubstitutions(false)])
241                 modernTag = WebMenuItemTagShowSubstitutions;
242             else if ([title isEqualToString:contextMenuItemTagSmartCopyPaste()])
243                 modernTag = WebMenuItemTagSmartCopyPaste;
244             else if ([title isEqualToString:contextMenuItemTagSmartQuotes()])
245                 modernTag = WebMenuItemTagSmartQuotes;
246             else if ([title isEqualToString:contextMenuItemTagSmartDashes()])
247                 modernTag = WebMenuItemTagSmartDashes;
248             else if ([title isEqualToString:contextMenuItemTagSmartLinks()])
249                 modernTag = WebMenuItemTagSmartLinks;
250             else if ([title isEqualToString:contextMenuItemTagTextReplacement()])
251                 modernTag = WebMenuItemTagTextReplacement;
252             else if ([title isEqualToString:contextMenuItemTagTransformationsMenu()])
253                 modernTag = WebMenuItemTagTransformationsMenu;
254             else if ([title isEqualToString:contextMenuItemTagMakeUpperCase()])
255                 modernTag = WebMenuItemTagMakeUpperCase;
256             else if ([title isEqualToString:contextMenuItemTagMakeLowerCase()])
257                 modernTag = WebMenuItemTagMakeLowerCase;
258             else if ([title isEqualToString:contextMenuItemTagCapitalize()])
259                 modernTag = WebMenuItemTagCapitalize;
260             else {
261             // We don't expect WebMenuItemTagOther for any items other than the ones we explicitly handle.
262             // There's nothing to prevent an app from applying this tag, but they are supposed to only
263             // use tags in the range starting with WebMenuItemBaseApplicationTag=10000
264                 ASSERT_NOT_REACHED();
265             }
266         } else if (preVersion3Client) {
267             // Restore the new API tag for items on which we temporarily set the old SPI tag. The old SPI tag was
268             // needed to avoid confusing clients linked against earlier WebKits; the new API tag is needed for
269             // WebCore to handle the menu items appropriately (without needing to know about the old SPI tags).
270             switch (tag) {
271                 case OldWebMenuItemTagSearchInSpotlight:
272                     modernTag = WebMenuItemTagSearchInSpotlight;
273                     break;
274                 case OldWebMenuItemTagSearchWeb:
275                     modernTag = WebMenuItemTagSearchWeb;
276                     break;
277                 case OldWebMenuItemTagLookUpInDictionary:
278                     modernTag = WebMenuItemTagLookUpInDictionary;
279                     break;
280                 default:
281                     break;
282             }
283         }
284         
285         if (modernTag != tag)
286             [item setTag:modernTag];        
287     }
288 }
289
290 NSMutableArray* WebContextMenuClient::getCustomMenuFromDefaultItems(ContextMenu* defaultMenu)
291 {
292     id delegate = [m_webView UIDelegate];
293     SEL selector = @selector(webView:contextMenuItemsForElement:defaultMenuItems:);
294     if (![delegate respondsToSelector:selector])
295         return defaultMenu->platformDescription();
296
297     NSDictionary *element = [[[WebElementDictionary alloc] initWithHitTestResult:[m_webView page]->contextMenuController().hitTestResult()] autorelease];
298
299     BOOL preVersion3Client = isPreVersion3Client();
300     if (preVersion3Client) {
301         DOMNode *node = [element objectForKey:WebElementDOMNodeKey];
302         if ([node isKindOfClass:[DOMHTMLInputElement class]] && [(DOMHTMLInputElement *)node _isTextField])
303             return defaultMenu->platformDescription();
304         if ([node isKindOfClass:[DOMHTMLTextAreaElement class]])
305             return defaultMenu->platformDescription();
306     }
307
308     NSMutableArray *defaultMenuItems = defaultMenu->platformDescription();
309
310     unsigned defaultItemsCount = [defaultMenuItems count];
311     for (unsigned i = 0; i < defaultItemsCount; ++i)
312         [[defaultMenuItems objectAtIndex:i] setRepresentedObject:element];
313
314     NSMutableArray *savedItems = [fixMenusToSendToOldClients(defaultMenuItems) retain];
315     NSArray *delegateSuppliedItems = CallUIDelegate(m_webView, selector, element, defaultMenuItems);
316     NSMutableArray *newMenuItems = [delegateSuppliedItems mutableCopy];
317     fixMenusReceivedFromOldClients(newMenuItems, savedItems);
318     [savedItems release];
319     return [newMenuItems autorelease];
320 }
321
322 void WebContextMenuClient::contextMenuItemSelected(ContextMenuItem* item, const ContextMenu* parentMenu)
323 {
324     id delegate = [m_webView UIDelegate];
325     SEL selector = @selector(webView:contextMenuItemSelected:forElement:);
326     if ([delegate respondsToSelector:selector]) {
327         NSDictionary *element = [[WebElementDictionary alloc] initWithHitTestResult:[m_webView page]->contextMenuController().hitTestResult()];
328
329         CallUIDelegate(m_webView, selector, item->platformDescription(), element);
330
331         [element release];
332     }
333 }
334
335 void WebContextMenuClient::downloadURL(const URL& url)
336 {
337     [m_webView _downloadURL:url];
338 }
339
340 void WebContextMenuClient::searchWithSpotlight()
341 {
342     [m_webView _searchWithSpotlightFromMenu:nil];
343 }
344
345 void WebContextMenuClient::searchWithGoogle(const Frame*)
346 {
347     [m_webView _searchWithGoogleFromMenu:nil];
348 }
349
350 void WebContextMenuClient::lookUpInDictionary(Frame* frame)
351 {
352     WebHTMLView* htmlView = (WebHTMLView*)[[kit(frame) frameView] documentView];
353     if(![htmlView isKindOfClass:[WebHTMLView class]])
354         return;
355     [htmlView _lookUpInDictionaryFromMenu:nil];
356 }
357
358 bool WebContextMenuClient::isSpeaking()
359 {
360     return [NSApp isSpeaking];
361 }
362
363 void WebContextMenuClient::speak(const String& string)
364 {
365     [NSApp speakString:[[(NSString*)string copy] autorelease]];
366 }
367
368 void WebContextMenuClient::stopSpeaking()
369 {
370     [NSApp stopSpeaking:nil];
371 }
372
373 ContextMenuItem WebContextMenuClient::shareMenuItem(const HitTestResult& hitTestResult)
374 {
375     if (![[NSMenuItem class] respondsToSelector:@selector(standardShareMenuItemWithItems:)])
376         return ContextMenuItem();
377
378     Node* node = hitTestResult.innerNonSharedNode();
379     if (!node)
380         return ContextMenuItem();
381
382     Frame* frame = node->document().frame();
383     if (!frame)
384         return ContextMenuItem();
385
386     URL downloadableMediaURL;
387     if (!hitTestResult.absoluteMediaURL().isEmpty() && hitTestResult.isDownloadableMedia())
388         downloadableMediaURL = hitTestResult.absoluteMediaURL();
389
390     RetainPtr<NSImage> nsImage;
391     if (Image* image = hitTestResult.image()) {
392         if (RefPtr<SharedBuffer> buffer = image->data())
393             nsImage = adoptNS([[NSImage alloc] initWithData:[NSData dataWithBytes:buffer->data() length:buffer->size()]]);
394     }
395
396     return ContextMenuItem::shareMenuItem(hitTestResult.absoluteLinkURL(), downloadableMediaURL, nsImage.get(), hitTestResult.selectedText());
397 }
398
399 bool WebContextMenuClient::clientFloatRectForNode(Node& node, FloatRect& rect) const
400 {
401     RenderObject* renderer = node.renderer();
402     if (!renderer) {
403         // This method shouldn't be called in cases where the controlled node hasn't rendered.
404         ASSERT_NOT_REACHED();
405         return false;
406     }
407
408     if (!is<RenderBox>(*renderer))
409         return false;
410     auto& renderBox = downcast<RenderBox>(*renderer);
411
412     LayoutRect layoutRect = renderBox.clientBoxRect();
413     FloatQuad floatQuad = renderBox.localToAbsoluteQuad(FloatQuad(layoutRect));
414     rect = floatQuad.boundingBox();
415
416     return true;
417 }
418
419 #if ENABLE(SERVICE_CONTROLS)
420 void WebContextMenuClient::sharingServicePickerWillBeDestroyed(WebSharingServicePickerController &)
421 {
422     m_sharingServicePickerController = nil;
423 }
424
425 WebCore::FloatRect WebContextMenuClient::screenRectForCurrentSharingServicePickerItem(WebSharingServicePickerController &)
426 {
427     Page* page = [m_webView page];
428     if (!page)
429         return NSZeroRect;
430
431     Node* node = page->contextMenuController().context().hitTestResult().innerNode();
432     if (!node)
433         return NSZeroRect;
434
435     FrameView* frameView = node->document().view();
436     if (!frameView) {
437         // This method shouldn't be called in cases where the controlled node isn't in a rendered view.
438         ASSERT_NOT_REACHED();
439         return NSZeroRect;
440     }
441
442     FloatRect rect;
443     if (!clientFloatRectForNode(*node, rect))
444         return NSZeroRect;
445
446     // FIXME: https://webkit.org/b/132915
447     // Ideally we'd like to convert the content rect to screen coordinates without the lossy float -> int conversion.
448     // Creating a rounded int rect works well in practice, but might still lead to off-by-one-pixel problems in edge cases.
449     IntRect intRect = roundedIntRect(rect);
450     return frameView->contentsToScreen(intRect);
451 }
452
453 RetainPtr<NSImage> WebContextMenuClient::imageForCurrentSharingServicePickerItem(WebSharingServicePickerController &)
454 {
455     Page* page = [m_webView page];
456     if (!page)
457         return nil;
458
459     Node* node = page->contextMenuController().context().hitTestResult().innerNode();
460     if (!node)
461         return nil;
462
463     FrameView* frameView = node->document().view();
464     if (!frameView) {
465         // This method shouldn't be called in cases where the controlled node isn't in a rendered view.
466         ASSERT_NOT_REACHED();
467         return nil;
468     }
469
470     FloatRect rect;
471     if (!clientFloatRectForNode(*node, rect))
472         return nil;
473
474     std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(rect.size());
475     if (!buffer)
476         return nil;
477
478     VisibleSelection oldSelection = frameView->frame().selection().selection();
479     RefPtr<Range> range = Range::create(node->document(), Position(node, Position::PositionIsBeforeAnchor), Position(node, Position::PositionIsAfterAnchor));
480     frameView->frame().selection().setSelection(VisibleSelection(*range), FrameSelection::DoNotSetFocus);
481
482     PaintBehavior oldPaintBehavior = frameView->paintBehavior();
483     frameView->setPaintBehavior(PaintBehaviorSelectionOnly);
484
485     buffer->context().translate(-toFloatSize(rect.location()));
486     frameView->paintContents(buffer->context(), roundedIntRect(rect));
487
488     frameView->frame().selection().setSelection(oldSelection);
489     frameView->setPaintBehavior(oldPaintBehavior);
490
491     RefPtr<Image> image = buffer->copyImage(DontCopyBackingStore);
492     if (!image)
493         return nil;
494
495     return [[image->getNSImage() retain] autorelease];
496 }
497 #endif
498
499 NSMenu *WebContextMenuClient::contextMenuForEvent(NSEvent *event, NSView *view, bool& isServicesMenu)
500 {
501     isServicesMenu = false;
502
503     Page* page = [m_webView page];
504     if (!page)
505         return nil;
506
507 #if ENABLE(SERVICE_CONTROLS) && defined(__LP64__)
508     if (Image* image = page->contextMenuController().context().controlledImage()) {
509         ASSERT(page->contextMenuController().context().hitTestResult().innerNode());
510
511         RetainPtr<NSItemProvider> itemProvider = adoptNS([[NSItemProvider alloc] initWithItem:image->getNSImage() typeIdentifier:@"public.image"]);
512
513         bool isContentEditable = page->contextMenuController().context().hitTestResult().innerNode()->isContentEditable();
514         m_sharingServicePickerController = adoptNS([[WebSharingServicePickerController alloc] initWithItems:@[ itemProvider.get() ] includeEditorServices:isContentEditable client:this style:NSSharingServicePickerStyleRollover]);
515
516         isServicesMenu = true;
517         return [m_sharingServicePickerController menu];
518     }
519 #endif
520
521     return [view menuForEvent:event];
522 }
523
524 void WebContextMenuClient::showContextMenu()
525 {
526     Page* page = [m_webView page];
527     if (!page)
528         return;
529     Frame* frame = page->contextMenuController().hitTestResult().innerNodeFrame();
530     if (!frame)
531         return;
532     FrameView* frameView = frame->view();
533     if (!frameView)
534         return;
535
536     NSView* view = frameView->documentView();
537     IntPoint point = frameView->contentsToWindow(page->contextMenuController().hitTestResult().roundedPointInInnerNodeFrame());
538     NSEvent* event = [NSEvent mouseEventWithType:NSRightMouseDown location:point modifierFlags:0 timestamp:0 windowNumber:[[view window] windowNumber] context:0 eventNumber:0 clickCount:1 pressure:1];
539
540     // Show the contextual menu for this event.
541     bool isServicesMenu;
542     if (NSMenu *menu = contextMenuForEvent(event, view, isServicesMenu)) {
543         if (isServicesMenu)
544             [menu popUpMenuPositioningItem:nil atLocation:[view convertPoint:point toView:nil] inView:view];
545         else
546             [NSMenu popUpContextMenu:menu withEvent:event forView:view];
547     }
548 }
549
550 #endif // !PLATFORM(IOS)