<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[175178] trunk/Source/WebKit2</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/175178">175178</a></dd>
<dt>Author</dt> <dd>timothy_horton@apple.com</dd>
<dt>Date</dt> <dd>2014-10-24 14:08:20 -0700 (Fri, 24 Oct 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Mac][WebKit2] Move action menu code into its own file
https://bugs.webkit.org/show_bug.cgi?id=138034
&lt;rdar://problem/18758758&gt;

Reviewed by Dan Bernstein.

* UIProcess/API/mac/WKView.mm:
(-[WKView initWithFrame:context:configuration:webView:]):
Make a WKActionMenuController if needed.

(-[WKView prepareForMenu:withEvent:]):
(-[WKView willOpenMenu:withEvent:]):
(-[WKView didCloseMenu:withEvent:]):
(-[WKView _didPerformActionMenuHitTest:]):
Forward these to WKActionMenuController.

(-[WKView _openURLFromActionMenu:]): Deleted.
(-[WKView _addToReadingListFromActionMenu:]): Deleted.
(-[WKView _quickLookURLFromActionMenu:]): Deleted.
(-[WKView _createActionMenuItemForTag:]): Deleted.
(webKitBundleImageNamed): Deleted.
(-[WKView _copyImage:]): Deleted.
(-[WKView _saveImageToDownloads:]): Deleted.
(temporaryPhotosDirectoryPath): Deleted.
(pathToPhotoOnDisk): Deleted.
(-[WKView _addImageToPhotos:]): Deleted.
(-[WKView _defaultMenuItemsForImage]): Deleted.
(-[WKView _defaultMenuItems]): Deleted.
(-[WKView _updateActionMenu]): Deleted.
Moved to WKActionMenuController.

* UIProcess/mac/WKActionMenuController.h: Added.
* UIProcess/mac/WKActionMenuController.mm: Added.
(-[WKActionMenuController initWithPage:view:]):
(-[WKActionMenuController prepareForMenu:withEvent:]):
(-[WKActionMenuController willOpenMenu:withEvent:]):
(-[WKActionMenuController didCloseMenu:withEvent:]):
(-[WKActionMenuController didPerformActionMenuHitTest:]):
(-[WKActionMenuController _defaultMenuItemsForLink]):
(-[WKActionMenuController _openURLFromActionMenu:]):
(-[WKActionMenuController _addToReadingListFromActionMenu:]):
(-[WKActionMenuController _quickLookURLFromActionMenu:]):
(-[WKActionMenuController _defaultMenuItemsForImage]):
(-[WKActionMenuController _copyImage:]):
(-[WKActionMenuController _saveImageToDownloads:]):
(temporaryPhotosDirectoryPath):
(pathToPhotoOnDisk):
(-[WKActionMenuController _addImageToPhotos:]):
(-[WKActionMenuController _createActionMenuItemForTag:]):
(webKitBundleImageNamed):
(imageForResource:name::if):
(-[WKActionMenuController _updateActionMenuItems]):
Moved from WKView.

* WebKit2.xcodeproj/project.pbxproj:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPImacWKViewmm">trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm</a></li>
<li><a href="#trunkSourceWebKit2WebKit2xcodeprojprojectpbxproj">trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2UIProcessmacWKActionMenuControllerh">trunk/Source/WebKit2/UIProcess/mac/WKActionMenuController.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessmacWKActionMenuControllermm">trunk/Source/WebKit2/UIProcess/mac/WKActionMenuController.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (175177 => 175178)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2014-10-24 20:59:58 UTC (rev 175177)
+++ trunk/Source/WebKit2/ChangeLog        2014-10-24 21:08:20 UTC (rev 175178)
</span><span class="lines">@@ -1,3 +1,61 @@
</span><ins>+2014-10-24  Tim Horton  &lt;timothy_horton@apple.com&gt;
+
+        [Mac][WebKit2] Move action menu code into its own file
+        https://bugs.webkit.org/show_bug.cgi?id=138034
+        &lt;rdar://problem/18758758&gt;
+
+        Reviewed by Dan Bernstein.
+
+        * UIProcess/API/mac/WKView.mm:
+        (-[WKView initWithFrame:context:configuration:webView:]):
+        Make a WKActionMenuController if needed.
+
+        (-[WKView prepareForMenu:withEvent:]):
+        (-[WKView willOpenMenu:withEvent:]):
+        (-[WKView didCloseMenu:withEvent:]):
+        (-[WKView _didPerformActionMenuHitTest:]):
+        Forward these to WKActionMenuController.
+
+        (-[WKView _openURLFromActionMenu:]): Deleted.
+        (-[WKView _addToReadingListFromActionMenu:]): Deleted.
+        (-[WKView _quickLookURLFromActionMenu:]): Deleted.
+        (-[WKView _createActionMenuItemForTag:]): Deleted.
+        (webKitBundleImageNamed): Deleted.
+        (-[WKView _copyImage:]): Deleted.
+        (-[WKView _saveImageToDownloads:]): Deleted.
+        (temporaryPhotosDirectoryPath): Deleted.
+        (pathToPhotoOnDisk): Deleted.
+        (-[WKView _addImageToPhotos:]): Deleted.
+        (-[WKView _defaultMenuItemsForImage]): Deleted.
+        (-[WKView _defaultMenuItems]): Deleted.
+        (-[WKView _updateActionMenu]): Deleted.
+        Moved to WKActionMenuController.
+
+        * UIProcess/mac/WKActionMenuController.h: Added.
+        * UIProcess/mac/WKActionMenuController.mm: Added.
+        (-[WKActionMenuController initWithPage:view:]):
+        (-[WKActionMenuController prepareForMenu:withEvent:]):
+        (-[WKActionMenuController willOpenMenu:withEvent:]):
+        (-[WKActionMenuController didCloseMenu:withEvent:]):
+        (-[WKActionMenuController didPerformActionMenuHitTest:]):
+        (-[WKActionMenuController _defaultMenuItemsForLink]):
+        (-[WKActionMenuController _openURLFromActionMenu:]):
+        (-[WKActionMenuController _addToReadingListFromActionMenu:]):
+        (-[WKActionMenuController _quickLookURLFromActionMenu:]):
+        (-[WKActionMenuController _defaultMenuItemsForImage]):
+        (-[WKActionMenuController _copyImage:]):
+        (-[WKActionMenuController _saveImageToDownloads:]):
+        (temporaryPhotosDirectoryPath):
+        (pathToPhotoOnDisk):
+        (-[WKActionMenuController _addImageToPhotos:]):
+        (-[WKActionMenuController _createActionMenuItemForTag:]):
+        (webKitBundleImageNamed):
+        (imageForResource:name::if):
+        (-[WKActionMenuController _updateActionMenuItems]):
+        Moved from WKView.
+
+        * WebKit2.xcodeproj/project.pbxproj:
+
</ins><span class="cx"> 2014-10-24  Marcos Chavarría Teijeiro  &lt;chavarria1991@gmail.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [GTK] Implement is_selected method on WebKitHitTestResult
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPImacWKViewmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm (175177 => 175178)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm        2014-10-24 20:59:58 UTC (rev 175177)
+++ trunk/Source/WebKit2/UIProcess/API/mac/WKView.mm        2014-10-24 21:08:20 UTC (rev 175178)
</span><span class="lines">@@ -57,6 +57,7 @@
</span><span class="cx"> #import &quot;ViewGestureController.h&quot;
</span><span class="cx"> #import &quot;ViewSnapshotStore.h&quot;
</span><span class="cx"> #import &quot;WKAPICast.h&quot;
</span><ins>+#import &quot;WKActionMenuController.h&quot;
</ins><span class="cx"> #import &quot;WKActionMenuItemTypes.h&quot;
</span><span class="cx"> #import &quot;WKFullScreenWindowController.h&quot;
</span><span class="cx"> #import &quot;WKPrintingView.h&quot;
</span><span class="lines">@@ -76,8 +77,6 @@
</span><span class="cx"> #import &quot;WebProcessProxy.h&quot;
</span><span class="cx"> #import &quot;WebSystemInterface.h&quot;
</span><span class="cx"> #import &quot;_WKThumbnailViewInternal.h&quot;
</span><del>-#import &lt;ImageIO/ImageIO.h&gt;
-#import &lt;ImageKit/ImageKit.h&gt;
</del><span class="cx"> #import &lt;QuartzCore/QuartzCore.h&gt;
</span><span class="cx"> #import &lt;WebCore/AXObjectCache.h&gt;
</span><span class="cx"> #import &lt;WebCore/ColorMac.h&gt;
</span><span class="lines">@@ -89,7 +88,6 @@
</span><span class="cx"> #import &lt;WebCore/FileSystem.h&gt;
</span><span class="cx"> #import &lt;WebCore/KeyboardEvent.h&gt;
</span><span class="cx"> #import &lt;WebCore/LocalizedStrings.h&gt;
</span><del>-#import &lt;WebCore/NSSharingServicePickerSPI.h&gt;
</del><span class="cx"> #import &lt;WebCore/NSViewSPI.h&gt;
</span><span class="cx"> #import &lt;WebCore/PlatformEventFactoryMac.h&gt;
</span><span class="cx"> #import &lt;WebCore/PlatformScreen.h&gt;
</span><span class="lines">@@ -124,16 +122,6 @@
</span><span class="cx"> - (void)_maskRoundedBottomCorners:(NSRect)clipRect;
</span><span class="cx"> @end
</span><span class="cx"> 
</span><del>-@class QLPreviewBubble;
-@interface NSObject (WKQLPreviewBubbleDetails)
-@property (copy) NSArray * controls;
-@property NSSize maximumSize;
-@property NSRectEdge preferredEdge;
-@property (retain) IBOutlet NSWindow* parentWindow;
-- (void)showPreviewItem:(id)previewItem itemFrame:(NSRect)frame;
-- (void)setAutomaticallyCloseWithMask:(NSEventMask)autocloseMask filterMask:(NSEventMask)filterMask block:(void (^)(void))block;
-@end
-
</del><span class="cx"> #if USE(ASYNC_NSTEXTINPUTCLIENT)
</span><span class="cx"> @interface NSTextInputContext (WKNSTextInputContextDetails)
</span><span class="cx"> - (void)handleEvent:(NSEvent *)theEvent completionHandler:(void(^)(BOOL handled))completionHandler;
</span><span class="lines">@@ -153,9 +141,6 @@
</span><span class="cx"> CGError CGSGetScreenRectForWindow(CGSConnectionID cid, CGSWindowID wid, CGRect *rect);
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-SOFT_LINK_FRAMEWORK_IN_UMBRELLA(Quartz, ImageKit)
-SOFT_LINK_CLASS(ImageKit, IKSlideshow)
-
</del><span class="cx"> using namespace WebKit;
</span><span class="cx"> using namespace WebCore;
</span><span class="cx"> 
</span><span class="lines">@@ -176,13 +161,6 @@
</span><span class="cx"> };
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-// FIXME: This and all action menu related code should move to its own file.
-enum class ActionMenuState {
-    None = 0,
-    Pending,
-    Ready
-};
-
</del><span class="cx"> @interface WKViewData : NSObject {
</span><span class="cx"> @public
</span><span class="cx">     std::unique_ptr&lt;PageClientImpl&gt; _pageClient;
</span><span class="lines">@@ -279,9 +257,7 @@
</span><span class="cx">     _WKThumbnailView *_thumbnailView;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-    ActionMenuState _actionMenuState;
-    ActionMenuHitTestResult _actionMenuHitTestResult;
-    RetainPtr&lt;NSSharingServicePicker&gt; _actionMenuSharingServicePicker;
</del><ins>+    RetainPtr&lt;WKActionMenuController&gt; _actionMenuController;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> @end
</span><span class="lines">@@ -328,6 +304,8 @@
</span><span class="cx"> 
</span><span class="cx"> - (void)dealloc
</span><span class="cx"> {
</span><ins>+    [_data-&gt;_actionMenuController willDestroyView:self];
+
</ins><span class="cx">     _data-&gt;_page-&gt;close();
</span><span class="cx"> 
</span><span class="cx"> #if WK_API_ENABLED
</span><span class="lines">@@ -3557,8 +3535,9 @@
</span><span class="cx">     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillTerminate:) name:NSApplicationWillTerminateNotification object:NSApp];
</span><span class="cx"> 
</span><span class="cx">     if ([self respondsToSelector:@selector(setActionMenu:)]) {
</span><del>-        RetainPtr&lt;NSMenu&gt; actionMenu = adoptNS([[NSMenu alloc] init]);
-        [self setActionMenu:actionMenu.get()];
</del><ins>+        RetainPtr&lt;NSMenu&gt; menu = adoptNS([[NSMenu alloc] init]);
+        self.actionMenu = menu.get();
+        _data-&gt;_actionMenuController = adoptNS([[WKActionMenuController alloc] initWithPage:*_data-&gt;_page view:self]);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     return self;
</span><span class="lines">@@ -3658,324 +3637,26 @@
</span><span class="cx">         _data-&gt;_gestureController-&gt;removeSwipeSnapshot();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-- (void)_openURLFromActionMenu:(id)sender
</del><ins>+- (void)prepareForMenu:(NSMenu *)menu withEvent:(NSEvent *)event
</ins><span class="cx"> {
</span><del>-    WebHitTestResult* hitTestResult = _data-&gt;_page-&gt;activeActionMenuHitTestResult();
-    if (!hitTestResult)
-        return;
-
-    NSURL *url = [NSURL URLWithString:hitTestResult-&gt;absoluteLinkURL()];
-    [[NSWorkspace sharedWorkspace] openURL:url];
</del><ins>+    [_data-&gt;_actionMenuController prepareForMenu:menu withEvent:event];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-- (void)_addToReadingListFromActionMenu:(id)sender
</del><ins>+- (void)willOpenMenu:(NSMenu *)menu withEvent:(NSEvent *)event
</ins><span class="cx"> {
</span><del>-    WebHitTestResult* hitTestResult = _data-&gt;_page-&gt;activeActionMenuHitTestResult();
-    if (!hitTestResult)
-        return;
-
-    NSURL *url = [NSURL URLWithString:hitTestResult-&gt;absoluteLinkURL()];
-    NSSharingService *service = [NSSharingService sharingServiceNamed:NSSharingServiceNameAddToSafariReadingList];
-    [service performWithItems:@[ url ]];
</del><ins>+    [_data-&gt;_actionMenuController willOpenMenu:menu withEvent:event];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-- (void)_quickLookURLFromActionMenu:(id)sender
</del><ins>+- (void)didCloseMenu:(NSMenu *)menu withEvent:(NSEvent *)event
</ins><span class="cx"> {
</span><del>-    WebHitTestResult* hitTestResult = _data-&gt;_page-&gt;activeActionMenuHitTestResult();
-    if (!hitTestResult)
-        return;
-
-    NSRect itemFrame = [self convertRect:hitTestResult-&gt;elementBoundingBox() toView:nil];
-    NSSize maximumPreviewSize = NSMakeSize(self.bounds.size.width * 0.75, self.bounds.size.height * 0.75);
-
-    RetainPtr&lt;QLPreviewBubble&gt; bubble = adoptNS([[NSClassFromString(@&quot;QLPreviewBubble&quot;) alloc] init]);
-    [bubble setParentWindow:self.window];
-    [bubble setMaximumSize:maximumPreviewSize];
-    [bubble setPreferredEdge:NSMaxYEdge];
-    [bubble setControls:@[ ]];
-    NSEventMask filterMask = NSAnyEventMask &amp; ~(NSAppKitDefinedMask | NSSystemDefinedMask | NSApplicationDefinedMask | NSMouseEnteredMask | NSMouseExitedMask);
-    NSEventMask autocloseMask = NSLeftMouseDownMask | NSRightMouseDownMask | NSKeyDownMask;
-    [bubble setAutomaticallyCloseWithMask:autocloseMask filterMask:filterMask block:[bubble] {
-        [bubble close];
-    }];
-    [bubble showPreviewItem:[NSURL URLWithString:hitTestResult-&gt;absoluteLinkURL()] itemFrame:itemFrame];
</del><ins>+    [_data-&gt;_actionMenuController didCloseMenu:menu withEvent:event];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-- (RetainPtr&lt;NSMenuItem&gt;)_createActionMenuItemForTag:(uint32_t)tag
-{
-    SEL selector = nil;
-    NSString *title = nil;
-    NSImage *image = nil;
-
-    switch (tag) {
-    case kWKContextActionItemTagOpenLinkInDefaultBrowser:
-        selector = @selector(_openURLFromActionMenu:);
-        title = @&quot;Open&quot;;
-        image = webKitBundleImageNamed(@&quot;OpenInNewWindowTemplate&quot;);
-        break;
-
-    case kWKContextActionItemTagPreviewLink:
-        selector = @selector(_quickLookURLFromActionMenu:);
-        title = @&quot;Preview&quot;;
-        image = [NSImage imageNamed:NSImageNameQuickLookTemplate];
-        break;
-
-    case kWKContextActionItemTagAddLinkToSafariReadingList:
-        selector = @selector(_addToReadingListFromActionMenu:);
-        title = @&quot;Add to Safari Reading List&quot;;
-        image = [NSImage imageNamed:NSImageNameBookmarksTemplate];
-        break;
-
-    case kWKContextActionItemTagCopyImage:
-        selector = @selector(_copyImage:);
-        title = @&quot;Copy&quot;;
-        image = webKitBundleImageNamed(@&quot;CopyImageTemplate&quot;);
-        break;
-
-    case kWKContextActionItemTagAddImageToPhotos:
-        selector = @selector(_addImageToPhotos:);
-        title = @&quot;Add to Photos&quot;;
-        image = webKitBundleImageNamed(@&quot;AddImageToPhotosTemplate&quot;);
-        break;
-
-    case kWKContextActionItemTagSaveImageToDownloads:
-        selector = @selector(_saveImageToDownloads:);
-        title = @&quot;Save to Downloads&quot;;
-        image = webKitBundleImageNamed(@&quot;SaveImageToDownloadsTemplate&quot;);
-        break;
-
-    case kWKContextActionItemTagShareImage:
-        title = @&quot;Share&quot;;
-        image = webKitBundleImageNamed(@&quot;ShareImageTemplate&quot;);
-        break;
-
-    default:
-        ASSERT_NOT_REACHED();
-        return nil;
-    }
-
-    RetainPtr&lt;NSMenuItem&gt; item = adoptNS([[NSMenuItem alloc] initWithTitle:title action:selector keyEquivalent:@&quot;&quot;]);
-    [item setImage:image];
-    [item setTarget:self];
-    [item setTag:tag];
-    return item;
-}
-
-static NSImage *webKitBundleImageNamed(NSString *name)
-{
-    return [[NSBundle bundleForClass:[WKView class]] imageForResource:name];
-}
-
-- (NSArray *)_defaultMenuItemsForLink
-{
-    WebHitTestResult* hitTestResult = _data-&gt;_page-&gt;activeActionMenuHitTestResult();
-    if (!hitTestResult)
-        return @[ ];
-
-    if (!WebCore::protocolIsInHTTPFamily(hitTestResult-&gt;absoluteLinkURL()))
-        return @[ ];
-
-    RetainPtr&lt;NSMenuItem&gt; openLinkItem = [self _createActionMenuItemForTag:kWKContextActionItemTagOpenLinkInDefaultBrowser];
-    RetainPtr&lt;NSMenuItem&gt; previewLinkItem = [self _createActionMenuItemForTag:kWKContextActionItemTagPreviewLink];
-    RetainPtr&lt;NSMenuItem&gt; readingListItem = [self _createActionMenuItemForTag:kWKContextActionItemTagAddLinkToSafariReadingList];
-
-    // FIXME: The separator item is required to work around &lt;rdar://18684207&gt;.
-    return @[openLinkItem.get(), previewLinkItem.get(), [NSMenuItem separatorItem], readingListItem.get()];
-}
-
-- (void)_copyImage:(id)sender
-{
-    WebHitTestResult* hitTestResult = _data-&gt;_page-&gt;activeActionMenuHitTestResult();
-    if (!hitTestResult)
-        return;
-
-    RefPtr&lt;ShareableBitmap&gt; bitmap = _data-&gt;_actionMenuHitTestResult.image;
-    if (!bitmap)
-        return;
-
-    RetainPtr&lt;CGImageRef&gt; image = bitmap-&gt;makeCGImage();
-    RetainPtr&lt;NSImage&gt; nsImage = adoptNS([[NSImage alloc] initWithCGImage:image.get() size:NSZeroSize]);
-    [[NSPasteboard generalPasteboard] clearContents];
-    [[NSPasteboard generalPasteboard] writeObjects:@[ nsImage.get() ]];
-}
-
-- (void)_saveImageToDownloads:(id)sender
-{
-    WebHitTestResult* hitTestResult = _data-&gt;_page-&gt;activeActionMenuHitTestResult();
-    if (!hitTestResult)
-        return;
-
-    _data-&gt;_page-&gt;process().context().download(_data-&gt;_page.get(), URL(URL(), hitTestResult-&gt;absoluteImageURL()));
-}
-
-static NSString *temporaryPhotosDirectoryPath()
-{
-    static NSString *temporaryPhotosDirectoryPath;
-
-    if (!temporaryPhotosDirectoryPath) {
-        NSString *temporaryDirectoryTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@&quot;WebKitPhotos-XXXXXX&quot;];
-        CString templateRepresentation = [temporaryDirectoryTemplate fileSystemRepresentation];
-
-        if (mkdtemp(templateRepresentation.mutableData()))
-            temporaryPhotosDirectoryPath = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:templateRepresentation.data() length:templateRepresentation.length()] copy];
-    }
-
-    return temporaryPhotosDirectoryPath;
-}
-
-static NSString *pathToPhotoOnDisk(NSString *suggestedFilename)
-{
-    NSString *photoDirectoryPath = temporaryPhotosDirectoryPath();
-    if (!photoDirectoryPath) {
-        WTFLogAlways(&quot;Cannot create temporary photo download directory.&quot;);
-        return nil;
-    }
-
-    NSString *path = [photoDirectoryPath stringByAppendingPathComponent:suggestedFilename];
-
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    if ([fileManager fileExistsAtPath:path]) {
-        NSString *pathTemplatePrefix = [photoDirectoryPath stringByAppendingPathComponent:@&quot;XXXXXX-&quot;];
-        NSString *pathTemplate = [pathTemplatePrefix stringByAppendingString:suggestedFilename];
-        CString pathTemplateRepresentation = [pathTemplate fileSystemRepresentation];
-
-        int fd = mkstemps(pathTemplateRepresentation.mutableData(), pathTemplateRepresentation.length() - strlen([pathTemplatePrefix fileSystemRepresentation]) + 1);
-        if (fd &lt; 0) {
-            WTFLogAlways(&quot;Cannot create photo file in the temporary directory (%@).&quot;, suggestedFilename);
-            return nil;
-        }
-
-        close(fd);
-        path = [fileManager stringWithFileSystemRepresentation:pathTemplateRepresentation.data() length:pathTemplateRepresentation.length()];
-    }
-
-    return path;
-}
-
-- (void)_addImageToPhotos:(id)sender
-{
-    // FIXME: We shouldn't even add the button if this is the case, for now.
-    if (![getIKSlideshowClass() canExportToApplication:(@&quot;com.apple.Photos&quot;)])
-        return;
-
-    WebHitTestResult* hitTestResult = _data-&gt;_page-&gt;activeActionMenuHitTestResult();
-    if (!hitTestResult)
-        return;
-
-    RefPtr&lt;ShareableBitmap&gt; bitmap = _data-&gt;_actionMenuHitTestResult.image;
-    if (!bitmap)
-        return;
-    RetainPtr&lt;CGImageRef&gt; image = bitmap-&gt;makeCGImage();
-
-    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-        NSString * const suggestedFilename = @&quot;image.jpg&quot;;
-
-        NSString *filePath = pathToPhotoOnDisk(suggestedFilename);
-        if (!filePath)
-            return;
-
-        NSURL *fileURL = [NSURL fileURLWithPath:filePath];
-        auto dest = adoptCF(CGImageDestinationCreateWithURL((CFURLRef)fileURL, kUTTypeJPEG, 1, nullptr));
-        CGImageDestinationAddImage(dest.get(), image.get(), nullptr);
-        CGImageDestinationFinalize(dest.get());
-
-        dispatch_async(dispatch_get_main_queue(), ^{
-            // This API provides no way to report failure, but if 18420778 is fixed so that it does, we should handle this.
-            [getIKSlideshowClass() exportSlideshowItem:filePath toApplication:(@&quot;com.apple.Photos&quot;)];
-        });
-    });
-}
-
-- (NSArray *)_defaultMenuItemsForImage
-{
-    WebHitTestResult* hitTestResult = _data-&gt;_page-&gt;activeActionMenuHitTestResult();
-    if (!hitTestResult)
-        return @[ ];
-
-    RetainPtr&lt;NSMenuItem&gt; copyImageItem = [self _createActionMenuItemForTag:kWKContextActionItemTagCopyImage];
-    RetainPtr&lt;NSMenuItem&gt; addToPhotosItem = [self _createActionMenuItemForTag:kWKContextActionItemTagAddImageToPhotos];
-    RetainPtr&lt;NSMenuItem&gt; saveToDownloadsItem = [self _createActionMenuItemForTag:kWKContextActionItemTagSaveImageToDownloads];
-    RetainPtr&lt;NSMenuItem&gt; shareItem = [self _createActionMenuItemForTag:kWKContextActionItemTagShareImage];
-
-    if (RefPtr&lt;ShareableBitmap&gt; bitmap = _data-&gt;_actionMenuHitTestResult.image) {
-        RetainPtr&lt;CGImageRef&gt; image = bitmap-&gt;makeCGImage();
-        RetainPtr&lt;NSImage&gt; nsImage = adoptNS([[NSImage alloc] initWithCGImage:image.get() size:NSZeroSize]);
-        _data-&gt;_actionMenuSharingServicePicker = adoptNS([[NSSharingServicePicker alloc] initWithItems:@[ nsImage.get() ]]);
-        [shareItem setSubmenu:[_data-&gt;_actionMenuSharingServicePicker menu]];
-     }
-
-    return @[copyImageItem.get(), addToPhotosItem.get(), saveToDownloadsItem.get(), shareItem.get()];
-}
-
-- (NSArray *)_defaultMenuItems
-{
-    if (WebHitTestResult* hitTestResult = _data-&gt;_page-&gt;activeActionMenuHitTestResult()) {
-        if (!hitTestResult-&gt;absoluteImageURL().isEmpty())
-            return [self _defaultMenuItemsForImage];
-        if (!hitTestResult-&gt;absoluteLinkURL().isEmpty())
-            return [self _defaultMenuItemsForLink];
-    }
-
-    return @[ ];
-}
-
-- (void)_updateActionMenu
-{
-    [[self actionMenu] removeAllItems];
-
-    NSArray *menuItems = [self _actionMenuItemsForHitTestResult:toAPI(_data-&gt;_page-&gt;activeActionMenuHitTestResult()) defaultActionMenuItems:[self _defaultMenuItems]];
-
-    for (NSMenuItem *item in menuItems)
-        [[self actionMenu] addItem:item];
-}
-
-- (void)prepareForMenu:(NSMenu *)menu withEvent:(NSEvent *)event
-{
-    if (menu != self.actionMenu)
-        return;
-
-    [self _updateActionMenu];
-
-    _data-&gt;_page-&gt;performActionMenuHitTestAtLocation([self convertPoint:[event locationInWindow] fromView:nil]);
-
-    _data-&gt;_actionMenuState = ActionMenuState::Pending;
-}
-
</del><span class="cx"> - (void)_didPerformActionMenuHitTest:(const ActionMenuHitTestResult&amp;)hitTestResult
</span><span class="cx"> {
</span><del>-    // FIXME: This needs to use the WebKit2 callback mechanism to avoid out-of-order replies.
-    _data-&gt;_actionMenuState = ActionMenuState::Ready;
-    _data-&gt;_actionMenuHitTestResult = hitTestResult;
</del><ins>+    [_data-&gt;_actionMenuController didPerformActionMenuHitTest:hitTestResult];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-- (void)willOpenMenu:(NSMenu *)menu withEvent:(NSEvent *)event
-{
-    if (menu != self.actionMenu)
-        return;
-
-    ASSERT(_data-&gt;_actionMenuState != ActionMenuState::None);
-
-    // FIXME: We need to be able to cancel this if the menu goes away.
-    // FIXME: Connection can be null if the process is closed; we should clean up better in that case.
-    if (_data-&gt;_actionMenuState == ActionMenuState::Pending) {
-        if (auto* connection = _data-&gt;_page-&gt;process().connection())
-            connection-&gt;waitForAndDispatchImmediately&lt;Messages::WebPageProxy::DidPerformActionMenuHitTest&gt;(_data-&gt;_page-&gt;pageID(), std::chrono::milliseconds(500));
-    }
-
-    if (_data-&gt;_actionMenuState == ActionMenuState::Ready)
-        [self _updateActionMenu];
-}
-
-- (void)didCloseMenu:(NSMenu *)menu withEvent:(NSEvent *)event
-{
-    if (menu != self.actionMenu)
-        return;
-
-    _data-&gt;_actionMenuState = ActionMenuState::None;
-    _data-&gt;_actionMenuHitTestResult = ActionMenuHitTestResult();
-    _data-&gt;_actionMenuSharingServicePicker = nil;
-}
-
</del><span class="cx"> @end
</span><span class="cx"> 
</span><span class="cx"> @implementation WKView (Private)
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessmacWKActionMenuControllerh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebKit2/UIProcess/mac/WKActionMenuController.h (0 => 175178)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/mac/WKActionMenuController.h                                (rev 0)
+++ trunk/Source/WebKit2/UIProcess/mac/WKActionMenuController.h        2014-10-24 21:08:20 UTC (rev 175178)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WKActionMenuController_h
+#define WKActionMenuController_h
+
+namespace WebKit {
+class WebPageProxy;
+struct ActionMenuHitTestResult;
+}
+
+@class WKView;
+
+@interface WKActionMenuController : NSObject
+
+- (instancetype)initWithPage:(WebKit::WebPageProxy&amp;)page view:(WKView *)wkView;
+- (void)willDestroyView:(WKView *)view;
+
+- (void)prepareForMenu:(NSMenu *)menu withEvent:(NSEvent *)event;
+- (void)willOpenMenu:(NSMenu *)menu withEvent:(NSEvent *)event;
+- (void)didCloseMenu:(NSMenu *)menu withEvent:(NSEvent *)event;
+
+- (void)didPerformActionMenuHitTest:(const WebKit::ActionMenuHitTestResult&amp;)hitTestResult;
+
+@end
+
+#endif // WKActionMenuController_h
</ins></span></pre></div>
<a id="trunkSourceWebKit2UIProcessmacWKActionMenuControllermm"></a>
<div class="addfile"><h4>Added: trunk/Source/WebKit2/UIProcess/mac/WKActionMenuController.mm (0 => 175178)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/mac/WKActionMenuController.mm                                (rev 0)
+++ trunk/Source/WebKit2/UIProcess/mac/WKActionMenuController.mm        2014-10-24 21:08:20 UTC (rev 175178)
</span><span class="lines">@@ -0,0 +1,401 @@
</span><ins>+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import &quot;config.h&quot;
+#import &quot;WKActionMenuController.h&quot;
+
+#if PLATFORM(MAC)
+
+#import &quot;ActionMenuHitTestResult.h&quot;
+#import &quot;WKActionMenuItemTypes.h&quot;
+#import &quot;WKNSURLExtras.h&quot;
+#import &quot;WKViewInternal.h&quot;
+#import &quot;WebContext.h&quot;
+#import &quot;WebPageMessages.h&quot;
+#import &quot;WebPageProxy.h&quot;
+#import &quot;WebPageProxyMessages.h&quot;
+#import &quot;WebProcessProxy.h&quot;
+#import &lt;ImageIO/ImageIO.h&gt;
+#import &lt;ImageKit/ImageKit.h&gt;
+#import &lt;WebCore/NSSharingServicePickerSPI.h&gt;
+#import &lt;WebCore/NSViewSPI.h&gt;
+#import &lt;WebCore/SoftLinking.h&gt;
+#import &lt;WebCore/URL.h&gt;
+
+// FIXME: This should move into an SPI header if it stays.
+@class QLPreviewBubble;
+@interface NSObject (WKQLPreviewBubbleDetails)
+@property (copy) NSArray * controls;
+@property NSSize maximumSize;
+@property NSRectEdge preferredEdge;
+@property (retain) IBOutlet NSWindow* parentWindow;
+- (void)showPreviewItem:(id)previewItem itemFrame:(NSRect)frame;
+- (void)setAutomaticallyCloseWithMask:(NSEventMask)autocloseMask filterMask:(NSEventMask)filterMask block:(void (^)(void))block;
+@end
+
+SOFT_LINK_FRAMEWORK_IN_UMBRELLA(Quartz, ImageKit)
+SOFT_LINK_CLASS(ImageKit, IKSlideshow)
+
+using namespace WebCore;
+using namespace WebKit;
+
+enum class ActionMenuState {
+    None = 0,
+    Pending,
+    Ready
+};
+
+@interface WKActionMenuController ()
+- (void)_updateActionMenuItems;
+@end
+
+@implementation WKActionMenuController {
+    WebPageProxy *_page;
+    WKView *_wkView;
+
+    ActionMenuState _state;
+    ActionMenuHitTestResult _hitTestResult;
+    RetainPtr&lt;NSSharingServicePicker&gt; _sharingServicePicker;
+}
+
+- (instancetype)initWithPage:(WebPageProxy&amp;)page view:(WKView *)wkView
+{
+    self = [super init];
+
+    if (!self)
+        return nil;
+
+    _page = &amp;page;
+    _wkView = wkView;
+
+    return self;
+}
+
+- (void)willDestroyView:(WKView *)view
+{
+    _page = nullptr;
+    _wkView = nullptr;
+}
+
+- (void)prepareForMenu:(NSMenu *)menu withEvent:(NSEvent *)event
+{
+    if (menu != _wkView.actionMenu)
+        return;
+
+    [self _updateActionMenuItems];
+
+    _page-&gt;performActionMenuHitTestAtLocation([_wkView convertPoint:[event locationInWindow] fromView:nil]);
+
+    _state = ActionMenuState::Pending;
+}
+
+- (void)willOpenMenu:(NSMenu *)menu withEvent:(NSEvent *)event
+{
+    if (menu != _wkView.actionMenu)
+        return;
+
+    ASSERT(_state != ActionMenuState::None);
+
+    // FIXME: We need to be able to cancel this if the menu goes away.
+    // FIXME: Connection can be null if the process is closed; we should clean up better in that case.
+    if (_state == ActionMenuState::Pending) {
+        if (auto* connection = _page-&gt;process().connection())
+            connection-&gt;waitForAndDispatchImmediately&lt;Messages::WebPageProxy::DidPerformActionMenuHitTest&gt;(_page-&gt;pageID(), std::chrono::milliseconds(500));
+    }
+
+    if (_state == ActionMenuState::Ready)
+        [self _updateActionMenuItems];
+}
+
+- (void)didCloseMenu:(NSMenu *)menu withEvent:(NSEvent *)event
+{
+    if (menu != _wkView.actionMenu)
+        return;
+    
+    _state = ActionMenuState::None;
+    _hitTestResult = ActionMenuHitTestResult();
+    _sharingServicePicker = nil;
+}
+
+- (void)didPerformActionMenuHitTest:(const ActionMenuHitTestResult&amp;)hitTestResult
+{
+    // FIXME: This needs to use the WebKit2 callback mechanism to avoid out-of-order replies.
+    _state = ActionMenuState::Ready;
+    _hitTestResult = hitTestResult;
+}
+
+#pragma mark Link actions
+
+- (NSArray *)_defaultMenuItemsForLink
+{
+    WebHitTestResult* hitTestResult = _page-&gt;activeActionMenuHitTestResult();
+    if (!WebCore::protocolIsInHTTPFamily(hitTestResult-&gt;absoluteLinkURL()))
+        return @[ ];
+
+    RetainPtr&lt;NSMenuItem&gt; openLinkItem = [self _createActionMenuItemForTag:kWKContextActionItemTagOpenLinkInDefaultBrowser];
+    RetainPtr&lt;NSMenuItem&gt; previewLinkItem = [self _createActionMenuItemForTag:kWKContextActionItemTagPreviewLink];
+    RetainPtr&lt;NSMenuItem&gt; readingListItem = [self _createActionMenuItemForTag:kWKContextActionItemTagAddLinkToSafariReadingList];
+
+    // FIXME: The separator item is required to work around &lt;rdar://18684207&gt;.
+    return @[ openLinkItem.get(), previewLinkItem.get(), [NSMenuItem separatorItem], readingListItem.get() ];
+}
+
+- (void)_openURLFromActionMenu:(id)sender
+{
+    WebHitTestResult* hitTestResult = _page-&gt;activeActionMenuHitTestResult();
+    [[NSWorkspace sharedWorkspace] openURL:[NSURL _web_URLWithWTFString:hitTestResult-&gt;absoluteLinkURL()]];
+}
+
+- (void)_addToReadingListFromActionMenu:(id)sender
+{
+    WebHitTestResult* hitTestResult = _page-&gt;activeActionMenuHitTestResult();
+    NSSharingService *service = [NSSharingService sharingServiceNamed:NSSharingServiceNameAddToSafariReadingList];
+    [service performWithItems:@[ [NSURL _web_URLWithWTFString:hitTestResult-&gt;absoluteLinkURL()] ]];
+}
+
+- (void)_quickLookURLFromActionMenu:(id)sender
+{
+    WebHitTestResult* hitTestResult = _page-&gt;activeActionMenuHitTestResult();
+    NSRect itemFrame = [_wkView convertRect:hitTestResult-&gt;elementBoundingBox() toView:nil];
+    NSSize maximumPreviewSize = NSMakeSize(_wkView.bounds.size.width * 0.75, _wkView.bounds.size.height * 0.75);
+
+    RetainPtr&lt;QLPreviewBubble&gt; bubble = adoptNS([[NSClassFromString(@&quot;QLPreviewBubble&quot;) alloc] init]);
+    [bubble setParentWindow:_wkView.window];
+    [bubble setMaximumSize:maximumPreviewSize];
+    [bubble setPreferredEdge:NSMaxYEdge];
+    [bubble setControls:@[ ]];
+    NSEventMask filterMask = NSAnyEventMask &amp; ~(NSAppKitDefinedMask | NSSystemDefinedMask | NSApplicationDefinedMask | NSMouseEnteredMask | NSMouseExitedMask);
+    NSEventMask autocloseMask = NSLeftMouseDownMask | NSRightMouseDownMask | NSKeyDownMask;
+    [bubble setAutomaticallyCloseWithMask:autocloseMask filterMask:filterMask block:[bubble] {
+        [bubble close];
+    }];
+    [bubble showPreviewItem:[NSURL _web_URLWithWTFString:hitTestResult-&gt;absoluteLinkURL()] itemFrame:itemFrame];
+}
+
+#pragma mark Image actions
+
+- (NSArray *)_defaultMenuItemsForImage
+{
+    RetainPtr&lt;NSMenuItem&gt; copyImageItem = [self _createActionMenuItemForTag:kWKContextActionItemTagCopyImage];
+    RetainPtr&lt;NSMenuItem&gt; addToPhotosItem = [self _createActionMenuItemForTag:kWKContextActionItemTagAddImageToPhotos];
+    RetainPtr&lt;NSMenuItem&gt; saveToDownloadsItem = [self _createActionMenuItemForTag:kWKContextActionItemTagSaveImageToDownloads];
+    RetainPtr&lt;NSMenuItem&gt; shareItem = [self _createActionMenuItemForTag:kWKContextActionItemTagShareImage];
+
+    if (RefPtr&lt;ShareableBitmap&gt; bitmap = _hitTestResult.image) {
+        RetainPtr&lt;CGImageRef&gt; image = bitmap-&gt;makeCGImage();
+        RetainPtr&lt;NSImage&gt; nsImage = adoptNS([[NSImage alloc] initWithCGImage:image.get() size:NSZeroSize]);
+        _sharingServicePicker = adoptNS([[NSSharingServicePicker alloc] initWithItems:@[ nsImage.get() ]]);
+        [shareItem setSubmenu:[_sharingServicePicker menu]];
+    }
+
+    return @[ copyImageItem.get(), addToPhotosItem.get(), saveToDownloadsItem.get(), shareItem.get() ];
+}
+
+- (void)_copyImage:(id)sender
+{
+    RefPtr&lt;ShareableBitmap&gt; bitmap = _hitTestResult.image;
+    if (!bitmap)
+        return;
+
+    RetainPtr&lt;CGImageRef&gt; image = bitmap-&gt;makeCGImage();
+    RetainPtr&lt;NSImage&gt; nsImage = adoptNS([[NSImage alloc] initWithCGImage:image.get() size:NSZeroSize]);
+    [[NSPasteboard generalPasteboard] clearContents];
+    [[NSPasteboard generalPasteboard] writeObjects:@[ nsImage.get() ]];
+}
+
+- (void)_saveImageToDownloads:(id)sender
+{
+    WebHitTestResult* hitTestResult = _page-&gt;activeActionMenuHitTestResult();
+    _page-&gt;process().context().download(_page, hitTestResult-&gt;absoluteImageURL());
+}
+
+// FIXME: We should try to share this with WebPageProxyMac's similar PDF functions.
+static NSString *temporaryPhotosDirectoryPath()
+{
+    static NSString *temporaryPhotosDirectoryPath;
+
+    if (!temporaryPhotosDirectoryPath) {
+        NSString *temporaryDirectoryTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@&quot;WebKitPhotos-XXXXXX&quot;];
+        CString templateRepresentation = [temporaryDirectoryTemplate fileSystemRepresentation];
+
+        if (mkdtemp(templateRepresentation.mutableData()))
+            temporaryPhotosDirectoryPath = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:templateRepresentation.data() length:templateRepresentation.length()] copy];
+    }
+
+    return temporaryPhotosDirectoryPath;
+}
+
+static NSString *pathToPhotoOnDisk(NSString *suggestedFilename)
+{
+    NSString *photoDirectoryPath = temporaryPhotosDirectoryPath();
+    if (!photoDirectoryPath) {
+        WTFLogAlways(&quot;Cannot create temporary photo download directory.&quot;);
+        return nil;
+    }
+
+    NSString *path = [photoDirectoryPath stringByAppendingPathComponent:suggestedFilename];
+
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    if ([fileManager fileExistsAtPath:path]) {
+        NSString *pathTemplatePrefix = [photoDirectoryPath stringByAppendingPathComponent:@&quot;XXXXXX-&quot;];
+        NSString *pathTemplate = [pathTemplatePrefix stringByAppendingString:suggestedFilename];
+        CString pathTemplateRepresentation = [pathTemplate fileSystemRepresentation];
+
+        int fd = mkstemps(pathTemplateRepresentation.mutableData(), pathTemplateRepresentation.length() - strlen([pathTemplatePrefix fileSystemRepresentation]) + 1);
+        if (fd &lt; 0) {
+            WTFLogAlways(&quot;Cannot create photo file in the temporary directory (%@).&quot;, suggestedFilename);
+            return nil;
+        }
+
+        close(fd);
+        path = [fileManager stringWithFileSystemRepresentation:pathTemplateRepresentation.data() length:pathTemplateRepresentation.length()];
+    }
+
+    return path;
+}
+
+- (void)_addImageToPhotos:(id)sender
+{
+    // FIXME: We shouldn't even add the button if this is the case, for now.
+    if (![getIKSlideshowClass() canExportToApplication:@&quot;com.apple.Photos&quot;])
+        return;
+
+    RefPtr&lt;ShareableBitmap&gt; bitmap = _hitTestResult.image;
+    if (!bitmap)
+        return;
+    RetainPtr&lt;CGImageRef&gt; image = bitmap-&gt;makeCGImage();
+
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        NSString * const suggestedFilename = @&quot;image.jpg&quot;;
+
+        NSString *filePath = pathToPhotoOnDisk(suggestedFilename);
+        if (!filePath)
+            return;
+
+        NSURL *fileURL = [NSURL fileURLWithPath:filePath];
+        auto dest = adoptCF(CGImageDestinationCreateWithURL((CFURLRef)fileURL, kUTTypeJPEG, 1, nullptr));
+        CGImageDestinationAddImage(dest.get(), image.get(), nullptr);
+        CGImageDestinationFinalize(dest.get());
+
+        dispatch_async(dispatch_get_main_queue(), ^{
+            // This API provides no way to report failure, but if 18420778 is fixed so that it does, we should handle this.
+            [getIKSlideshowClass() exportSlideshowItem:filePath toApplication:@&quot;com.apple.Photos&quot;];
+        });
+    });
+}
+
+#pragma mark Menu Items
+
+- (RetainPtr&lt;NSMenuItem&gt;)_createActionMenuItemForTag:(uint32_t)tag
+{
+    SEL selector = nullptr;
+    NSString *title = nil;
+    NSImage *image = nil;
+
+    // FIXME: These titles need to be localized.
+    switch (tag) {
+    case kWKContextActionItemTagOpenLinkInDefaultBrowser:
+        selector = @selector(_openURLFromActionMenu:);
+        title = @&quot;Open&quot;;
+        image = webKitBundleImageNamed(@&quot;OpenInNewWindowTemplate&quot;);
+        break;
+
+    case kWKContextActionItemTagPreviewLink:
+        selector = @selector(_quickLookURLFromActionMenu:);
+        title = @&quot;Preview&quot;;
+        image = [NSImage imageNamed:NSImageNameQuickLookTemplate];
+        break;
+
+    case kWKContextActionItemTagAddLinkToSafariReadingList:
+        selector = @selector(_addToReadingListFromActionMenu:);
+        title = @&quot;Add to Safari Reading List&quot;;
+        image = [NSImage imageNamed:NSImageNameBookmarksTemplate];
+        break;
+
+    case kWKContextActionItemTagCopyImage:
+        selector = @selector(_copyImage:);
+        title = @&quot;Copy&quot;;
+        image = webKitBundleImageNamed(@&quot;CopyImageTemplate&quot;);
+        break;
+
+    case kWKContextActionItemTagAddImageToPhotos:
+        selector = @selector(_addImageToPhotos:);
+        title = @&quot;Add to Photos&quot;;
+        image = webKitBundleImageNamed(@&quot;AddImageToPhotosTemplate&quot;);
+        break;
+
+    case kWKContextActionItemTagSaveImageToDownloads:
+        selector = @selector(_saveImageToDownloads:);
+        title = @&quot;Save to Downloads&quot;;
+        image = webKitBundleImageNamed(@&quot;SaveImageToDownloadsTemplate&quot;);
+        break;
+
+    case kWKContextActionItemTagShareImage:
+        title = @&quot;Share&quot;;
+        image = webKitBundleImageNamed(@&quot;ShareImageTemplate&quot;);
+        break;
+
+    default:
+        ASSERT_NOT_REACHED();
+        return nil;
+    }
+
+    RetainPtr&lt;NSMenuItem&gt; item = adoptNS([[NSMenuItem alloc] initWithTitle:title action:selector keyEquivalent:@&quot;&quot;]);
+    [item setImage:image];
+    [item setTarget:self];
+    [item setTag:tag];
+    return item;
+}
+
+static NSImage *webKitBundleImageNamed(NSString *name)
+{
+    return [[NSBundle bundleForClass:[WKView class]] imageForResource:name];
+}
+
+- (NSArray *)_defaultMenuItems
+{
+    if (WebHitTestResult* hitTestResult = _page-&gt;activeActionMenuHitTestResult()) {
+        if (!hitTestResult-&gt;absoluteImageURL().isEmpty())
+            return [self _defaultMenuItemsForImage];
+        if (!hitTestResult-&gt;absoluteLinkURL().isEmpty())
+            return [self _defaultMenuItemsForLink];
+    }
+
+    return @[ ];
+}
+
+- (void)_updateActionMenuItems
+{
+    [_wkView.actionMenu removeAllItems];
+
+    NSArray *menuItems = [_wkView _actionMenuItemsForHitTestResult:toAPI(_page-&gt;activeActionMenuHitTestResult()) defaultActionMenuItems:[self _defaultMenuItems]];
+    
+    for (NSMenuItem *item in menuItems)
+        [_wkView.actionMenu addItem:item];
+}
+
+@end
+
+#endif // PLATFORM(MAC)
</ins></span></pre></div>
<a id="trunkSourceWebKit2WebKit2xcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj (175177 => 175178)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj        2014-10-24 20:59:58 UTC (rev 175177)
+++ trunk/Source/WebKit2/WebKit2.xcodeproj/project.pbxproj        2014-10-24 21:08:20 UTC (rev 175178)
</span><span class="lines">@@ -564,6 +564,8 @@
</span><span class="cx">                 29CD55AB128E294F00133C85 /* WKAccessibilityWebPageObjectBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 29CD55A9128E294F00133C85 /* WKAccessibilityWebPageObjectBase.mm */; };
</span><span class="cx">                 29D55DF1161BF9F10031A2E3 /* WebPageGroupProxyMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 29D55DEF161BF9F10031A2E3 /* WebPageGroupProxyMessageReceiver.cpp */; };
</span><span class="cx">                 29D55DF2161BF9F10031A2E3 /* WebPageGroupProxyMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 29D55DF0161BF9F10031A2E3 /* WebPageGroupProxyMessages.h */; };
</span><ins>+                2D0730A219F9C7DA00E9D9C4 /* WKActionMenuController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D0730A019F9C7DA00E9D9C4 /* WKActionMenuController.mm */; };
+                2D0730A319F9C7DA00E9D9C4 /* WKActionMenuController.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D0730A119F9C7DA00E9D9C4 /* WKActionMenuController.h */; };
</ins><span class="cx">                 2D125C5E1857EA05003BA3CB /* ViewGestureController.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D125C5C1857EA05003BA3CB /* ViewGestureController.h */; };
</span><span class="cx">                 2D125C5F1857EA05003BA3CB /* ViewGestureControllerMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D125C5D1857EA05003BA3CB /* ViewGestureControllerMac.mm */; };
</span><span class="cx">                 2D1B5D5D185869C8006C6596 /* ViewGestureControllerMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B5D5B185869C8006C6596 /* ViewGestureControllerMessageReceiver.cpp */; };
</span><span class="lines">@@ -2560,6 +2562,8 @@
</span><span class="cx">                 29D55DEE161BF8780031A2E3 /* WebPageGroupProxy.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = WebPageGroupProxy.messages.in; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 29D55DEF161BF9F10031A2E3 /* WebPageGroupProxyMessageReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebPageGroupProxyMessageReceiver.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 29D55DF0161BF9F10031A2E3 /* WebPageGroupProxyMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebPageGroupProxyMessages.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                2D0730A019F9C7DA00E9D9C4 /* WKActionMenuController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKActionMenuController.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
+                2D0730A119F9C7DA00E9D9C4 /* WKActionMenuController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKActionMenuController.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 2D125C5C1857EA05003BA3CB /* ViewGestureController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewGestureController.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 2D125C5D1857EA05003BA3CB /* ViewGestureControllerMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewGestureControllerMac.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 2D1B5D5A18586599006C6596 /* ViewGestureController.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = ViewGestureController.messages.in; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -6627,6 +6631,8 @@
</span><span class="cx">                                 51D130571382F10500351EDD /* WebProcessProxyMac.mm */,
</span><span class="cx">                                 868160CD18763D4B0021E79D /* WindowServerConnection.h */,
</span><span class="cx">                                 868160CF187645370021E79D /* WindowServerConnection.mm */,
</span><ins>+                                2D0730A019F9C7DA00E9D9C4 /* WKActionMenuController.mm */,
+                                2D0730A119F9C7DA00E9D9C4 /* WKActionMenuController.h */,
</ins><span class="cx">                                 E1AEA22D14687BDB00804569 /* WKFullKeyboardAccessWatcher.h */,
</span><span class="cx">                                 E1AEA22E14687BDB00804569 /* WKFullKeyboardAccessWatcher.mm */,
</span><span class="cx">                                 CDCA85C7132ABA4E00E961DF /* WKFullScreenWindowController.h */,
</span><span class="lines">@@ -7077,6 +7083,7 @@
</span><span class="cx">                                 E1513C67166EABB200149FCB /* ChildProcessProxy.h in Headers */,
</span><span class="cx">                                 7CA254EB182993CE00FC8A41 /* WKBrowsingContextPolicyDelegate.h in Headers */,
</span><span class="cx">                                 37C4C08718149C5B003688B9 /* WKBackForwardListItem.h in Headers */,
</span><ins>+                                2D0730A319F9C7DA00E9D9C4 /* WKActionMenuController.h in Headers */,
</ins><span class="cx">                                 1F335BC0185B84F0001A201A /* WKWebProcessPlugInLoadDelegate.h in Headers */,
</span><span class="cx">                                 1F7506B41859164C00EC0FF7 /* WKWebProcessPlugInScriptWorld.h in Headers */,
</span><span class="cx">                                 1A43E82A188F3CDC009E4D30 /* _WKProcessPoolConfiguration.h in Headers */,
</span><span class="lines">@@ -9081,6 +9088,7 @@
</span><span class="cx">                                 518D2CAD12D5153B003BB93B /* WebBackForwardListItem.cpp in Sources */,
</span><span class="cx">                                 BC72B9FA11E6476B001EB4EA /* WebBackForwardListProxy.cpp in Sources */,
</span><span class="cx">                                 BC111A5A112F4FBB00337BAB /* WebChromeClient.cpp in Sources */,
</span><ins>+                                2D0730A219F9C7DA00E9D9C4 /* WKActionMenuController.mm in Sources */,
</ins><span class="cx">                                 868160D0187645570021E79D /* WindowServerConnection.mm in Sources */,
</span><span class="cx">                                 3F87B9BD158940120090FF62 /* WebColorChooser.cpp in Sources */,
</span><span class="cx">                                 A58B6F0918FCA733008CBA53 /* WKFileUploadPanel.mm in Sources */,
</span></span></pre>
</div>
</div>

</body>
</html>