1/* 2 * Copyright (C) 2014 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#import "WebSharingServicePickerController.h" 27 28#if ENABLE(SERVICE_CONTROLS) 29 30#import "WebContextMenuClient.h" 31#import "WebViewInternal.h" 32#import <AppKit/NSSharingService.h> 33#import <WebCore/BitmapImage.h> 34#import <WebCore/Document.h> 35#import <WebCore/Editor.h> 36#import <WebCore/FocusController.h> 37#import <WebCore/Frame.h> 38#import <WebCore/FrameSelection.h> 39#import <WebCore/ContextMenuController.h> 40#import <WebCore/Page.h> 41 42#if __has_include(<AppKit/NSSharingService_Private.h>) 43#import <AppKit/NSSharingService_Private.h> 44#else 45typedef enum { 46 NSSharingServicePickerStyleRollover = 1 47} NSSharingServicePickerStyle; 48 49@interface NSSharingServicePicker (Private) 50@property NSSharingServicePickerStyle style; 51- (NSMenu *)menu; 52@end 53#endif 54 55#if __has_include(<AppKit/NSItemProvider.h>) 56#import <AppKit/NSItemProvider.h> 57#else 58// FIXME: We should properly disable code that interacts with NSItemProvider/NSSharingServicePicker in 32-bit. 59@interface NSItemProvider : NSObject 60@property (copy, readonly) NSArray *registeredTypeIdentifiers; 61- (instancetype)initWithItem:(id <NSSecureCoding>)item typeIdentifier:(NSString *)typeIdentifier; 62- (void)loadItemForTypeIdentifier:(NSString *)typeIdentifier options:(NSDictionary *)options completionHandler:(void (^)(id <NSSecureCoding> item, NSError *error))completionHandler; 63@end 64#endif 65 66static NSString *serviceControlsPasteboardName = @"WebKitServiceControlsPasteboard"; 67 68using namespace WebCore; 69 70@implementation WebSharingServicePickerController 71 72- (instancetype)initWithData:(NSData *)data includeEditorServices:(BOOL)includeEditorServices menuClient:(WebContextMenuClient*)menuClient 73{ 74#ifndef __LP64__ 75 return nil; 76#else 77 if (!(self = [super init])) 78 return nil; 79 80 RetainPtr<NSItemProvider> itemProvider = adoptNS([[NSItemProvider alloc] initWithItem:data typeIdentifier:@"public.data"]); 81 82 _picker = adoptNS([[NSSharingServicePicker alloc] initWithItems:@[ itemProvider.get() ]]); 83 [_picker setStyle:NSSharingServicePickerStyleRollover]; 84 [_picker setDelegate:self]; 85 86 _includeEditorServices = includeEditorServices; 87 _menuClient = menuClient; 88 89 return self; 90#endif 91} 92 93- (void)clear 94{ 95 // Protect self from being dealloc'ed partway through this method. 96 RetainPtr<WebSharingServicePickerController> protector(self); 97 98 if (_menuClient) 99 _menuClient->clearSharingServicePickerController(); 100 101 _picker = nullptr; 102 _menuClient = nullptr; 103} 104 105- (NSMenu *)menu 106{ 107 return [_picker menu]; 108} 109 110- (void)didShareImageData:(NSData *)data confirmDataIsValidTIFFData:(BOOL)confirmData 111{ 112 Page* page = [_menuClient->webView() page]; 113 if (!page) 114 return; 115 116 if (confirmData) { 117 RetainPtr<NSImage> nsImage = adoptNS([[NSImage alloc] initWithData:data]); 118 if (!nsImage) { 119 LOG_ERROR("Shared image data cannot create a valid NSImage"); 120 return; 121 } 122 123 data = [nsImage TIFFRepresentation]; 124 } 125 126 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:serviceControlsPasteboardName]; 127 [pasteboard declareTypes:@[ NSPasteboardTypeTIFF ] owner:nil]; 128 [pasteboard setData:data forType:NSPasteboardTypeTIFF]; 129 130 if (Node* node = page->contextMenuController().context().hitTestResult().innerNode()) { 131 if (Frame* frame = node->document().frame()) 132 frame->editor().replaceNodeFromPasteboard(node, serviceControlsPasteboardName); 133 } 134 135 [self clear]; 136} 137 138#pragma mark NSSharingServicePickerDelegate methods 139 140- (NSArray *)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker sharingServicesForItems:(NSArray *)items mask:(NSSharingServiceMask)mask proposedSharingServices:(NSArray *)proposedServices 141{ 142 if (_includeEditorServices) 143 return proposedServices; 144 145 NSMutableArray *services = [[NSMutableArray alloc] initWithCapacity:[proposedServices count]]; 146 147 for (NSSharingService *service in proposedServices) { 148 if (service.type != NSSharingServiceTypeEditor) 149 [services addObject:service]; 150 } 151 152 return services; 153} 154 155- (id <NSSharingServiceDelegate>)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker delegateForSharingService:(NSSharingService *)sharingService 156{ 157 return self; 158} 159 160- (void)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker didChooseSharingService:(NSSharingService *)service 161{ 162 if (!service) 163 _menuClient->clearSharingServicePickerController(); 164} 165 166#pragma mark NSSharingServiceDelegate methods 167 168- (void)sharingService:(NSSharingService *)sharingService didShareItems:(NSArray *)items 169{ 170 // We only send one item, so we should only get one item back. 171 if ([items count] != 1) 172 return; 173 174 id item = [items objectAtIndex:0]; 175 176 if ([item isKindOfClass:[NSImage class]]) 177 [self didShareImageData:[item TIFFRepresentation] confirmDataIsValidTIFFData:NO]; 178#ifdef __LP64__ 179 else if ([item isKindOfClass:[NSItemProvider class]]) { 180 NSItemProvider *itemProvider = (NSItemProvider *)item; 181 NSString *itemUTI = itemProvider.registeredTypeIdentifiers.firstObject; 182 183 [itemProvider loadItemForTypeIdentifier:itemUTI options:nil completionHandler:^(id receivedData, NSError *dataError) { 184 if (!receivedData) { 185 LOG_ERROR("Did not receive data from NSItemProvider"); 186 return; 187 } 188 189 if (![receivedData isKindOfClass:[NSData class]]) { 190 LOG_ERROR("Data received from NSItemProvider is not of type NSData"); 191 return; 192 } 193 194 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 195 [self didShareImageData:receivedData confirmDataIsValidTIFFData:YES]; 196 }]; 197 198 }]; 199 } 200#endif 201 else 202 LOG_ERROR("sharingService:didShareItems: - Unknown item type returned"); 203} 204 205- (void)sharingService:(NSSharingService *)sharingService didFailToShareItems:(NSArray *)items error:(NSError *)error 206{ 207 [self clear]; 208} 209 210- (NSRect)sharingService:(NSSharingService *)sharingService sourceFrameOnScreenForShareItem:(id <NSPasteboardWriting>)item 211{ 212 if (!_menuClient) 213 return NSZeroRect; 214 215 return _menuClient->screenRectForHitTestNode(); 216} 217 218- (NSImage *)sharingService:(NSSharingService *)sharingService transitionImageForShareItem:(id <NSPasteboardWriting>)item contentRect:(NSRect *)contentRect 219{ 220 if (!_menuClient) 221 return nil; 222 223 return _menuClient->renderedImageForControlledImage(); 224} 225 226- (NSWindow *)sharingService:(NSSharingService *)sharingService sourceWindowForShareItems:(NSArray *)items sharingContentScope:(NSSharingContentScope *)sharingContentScope 227{ 228 return [_menuClient->webView() window]; 229} 230 231@end 232 233#endif 234