WebSharingServicePickerController.mm   [plain text]

/*
* 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 "WebSharingServicePickerController.h"

#if ENABLE(SERVICE_CONTROLS)

#import "WebContextMenuClient.h"
#import "WebViewInternal.h"
#import <WebCore/BitmapImage.h>
#import <WebCore/ContextMenuController.h>
#import <WebCore/Document.h>
#import <WebCore/Editor.h>
#import <WebCore/FocusController.h>
#import <WebCore/Frame.h>
#import <WebCore/FrameSelection.h>
#import <WebCore/Page.h>

static NSString *serviceControlsPasteboardName = @"WebKitServiceControlsPasteboard";

WebSharingServicePickerClient::WebSharingServicePickerClient(WebView *webView)
: m_webView(webView)
{
}

void WebSharingServicePickerClient::sharingServicePickerWillBeDestroyed(WebSharingServicePickerController &)
{
}

WebCore::Page* WebSharingServicePickerClient::pageForSharingServicePicker(WebSharingServicePickerController &)
{
return [m_webView page];
}

RetainPtr<NSWindow> WebSharingServicePickerClient::windowForSharingServicePicker(WebSharingServicePickerController &)
{
return [m_webView window];
}

WebCore::FloatRect WebSharingServicePickerClient::screenRectForCurrentSharingServicePickerItem(WebSharingServicePickerController &)
{
return WebCore::FloatRect();
}

RetainPtr<NSImage> WebSharingServicePickerClient::imageForCurrentSharingServicePickerItem(WebSharingServicePickerController &)
{
return nil;
}

@implementation WebSharingServicePickerController

#if ENABLE(SERVICE_CONTROLS)
- (instancetype)initWithItems:(NSArray *)items includeEditorServices:(BOOL)includeEditorServices client:(WebSharingServicePickerClient*)pickerClient style:(NSSharingServicePickerStyle)style
{
if (!(self = [super init]))
return nil;

_picker = adoptNS([[NSSharingServicePicker alloc] initWithItems:items]);
[_picker setStyle:style];
[_picker setDelegate:self];

_includeEditorServices = includeEditorServices;
_handleEditingReplacement = includeEditorServices;
_pickerClient = pickerClient;

return self;
}

- (instancetype)initWithSharingServicePicker:(NSSharingServicePicker *)sharingServicePicker client:(WebSharingServicePickerClient&)pickerClient
{
if (!(self = [super init]))
return nil;

_picker = sharingServicePicker;
[_picker setDelegate:self];

_includeEditorServices = YES;
_pickerClient = &pickerClient;

return self;
}
#endif // ENABLE(SERVICE_CONTROLS)

- (void)clear
{
// Protect self from being dealloc'ed partway through this method.
RetainPtr<WebSharingServicePickerController> protector(self);

if (_pickerClient)
_pickerClient->sharingServicePickerWillBeDestroyed(*self);

_picker = nullptr;
_pickerClient = nullptr;
}

- (NSMenu *)menu
{
return [_picker menu];
}

- (void)didShareImageData:(NSData *)data confirmDataIsValidTIFFData:(BOOL)confirmData
{
auto* page = _pickerClient->pageForSharingServicePicker(*self);
if (!page)
return;

if (confirmData) {
RetainPtr<NSImage> nsImage = adoptNS([[NSImage alloc] initWithData:data]);
if (!nsImage) {
LOG_ERROR("Shared image data cannot create a valid NSImage");
return;
}

data = [nsImage TIFFRepresentation];
}

NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:serviceControlsPasteboardName];
[pasteboard declareTypes:@[ NSPasteboardTypeTIFF ] owner:nil];
[pasteboard setData:data forType:NSPasteboardTypeTIFF];

if (auto* node = page->contextMenuController().context().hitTestResult().innerNode()) {
if (auto* frame = node->document().frame())
frame->editor().replaceNodeFromPasteboard(node, serviceControlsPasteboardName);
}

[self clear];
}

#pragma mark NSSharingServicePickerDelegate methods

- (NSArray *)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker sharingServicesForItems:(NSArray *)items mask:(NSSharingServiceMask)mask proposedSharingServices:(NSArray *)proposedServices
{
if (_includeEditorServices)
return proposedServices;

NSMutableArray *services = [NSMutableArray arrayWithCapacity:proposedServices.count];

for (NSSharingService *service in proposedServices) {
if (service.type != NSSharingServiceTypeEditor)
[services addObject:service];
}

return services;
}

- (id <NSSharingServiceDelegate>)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker delegateForSharingService:(NSSharingService *)sharingService
{
return self;
}

- (void)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker didChooseSharingService:(NSSharingService *)service
{
if (!service)
_pickerClient->sharingServicePickerWillBeDestroyed(*self);
}

#pragma mark NSSharingServiceDelegate methods

- (void)sharingService:(NSSharingService *)sharingService didShareItems:(NSArray *)items
{
if (!_handleEditingReplacement)
return;

// We only send one item, so we should only get one item back.
if ([items count] != 1)
return;

id item = [items objectAtIndex:0];

if ([item isKindOfClass:[NSImage class]])
[self didShareImageData:[item TIFFRepresentation] confirmDataIsValidTIFFData:NO];
else if ([item isKindOfClass:[NSItemProvider class]]) {
NSItemProvider *itemProvider = (NSItemProvider *)item;
NSString *itemUTI = itemProvider.registeredTypeIdentifiers.firstObject;

[itemProvider loadItemForTypeIdentifier:itemUTI options:nil completionHandler:^(id receivedData, NSError *dataError) {
if (!receivedData) {
LOG_ERROR("Did not receive data from NSItemProvider");
return;
}

if (![receivedData isKindOfClass:[NSData class]]) {
LOG_ERROR("Data received from NSItemProvider is not of type NSData");
return;
}

[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self didShareImageData:receivedData confirmDataIsValidTIFFData:YES];
}];

}];
}
else if ([item isKindOfClass:[NSAttributedString class]]) {
auto& frame = _pickerClient->pageForSharingServicePicker(*self)->focusController().focusedOrMainFrame();
frame.editor().replaceSelectionWithAttributedString(item);
} else
LOG_ERROR("sharingService:didShareItems: - Unknown item type returned\n");
}

- (void)sharingService:(NSSharingService *)sharingService didFailToShareItems:(NSArray *)items error:(NSError *)error
{
[self clear];
}

- (NSRect)sharingService:(NSSharingService *)sharingService sourceFrameOnScreenForShareItem:(id <NSPasteboardWriting>)item
{
if (!_pickerClient)
return NSZeroRect;

return _pickerClient->screenRectForCurrentSharingServicePickerItem(*self);
}

- (NSImage *)sharingService:(NSSharingService *)sharingService transitionImageForShareItem:(id <NSPasteboardWriting>)item contentRect:(NSRect *)contentRect
{
if (!_pickerClient)
return nil;

return _pickerClient->imageForCurrentSharingServicePickerItem(*self).get();
}

- (NSWindow *)sharingService:(NSSharingService *)sharingService sourceWindowForShareItems:(NSArray *)items sharingContentScope:(NSSharingContentScope *)sharingContentScope
{
return _pickerClient->windowForSharingServicePicker(*self).get();
}

@end

#endif // ENABLE(SERVICE_CONTROLS)