2 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "WebDragClient.h"
29 #import "PasteboardTypes.h"
30 #import "ShareableBitmap.h"
31 #import "WebCoreArgumentCoders.h"
33 #import "WebPageProxyMessages.h"
34 #import <WebCore/CachedImage.h>
35 #import <WebCore/DOMElementInternal.h>
36 #import <WebCore/DOMPrivate.h>
37 #import <WebCore/DragController.h>
38 #import <WebCore/Frame.h>
39 #import <WebCore/FrameView.h>
40 #import <WebCore/GraphicsContext.h>
41 #import <WebCore/LegacyWebArchive.h>
42 #import <WebCore/RenderImage.h>
43 #import <WebCore/ResourceHandle.h>
44 #import <WebCore/StringTruncator.h>
45 #import <WebKit/WebArchive.h>
46 #import <WebKit/WebKitNSStringExtras.h>
47 #import <WebKit/WebNSFileManagerExtras.h>
48 #import <WebKit/WebNSPasteboardExtras.h>
49 #import <WebKit/WebNSURLExtras.h>
50 #import <WebKitSystemInterface.h>
51 #import <wtf/StdLibExtras.h>
53 using namespace WebCore;
54 using namespace WebKit;
56 // Internal AppKit class. If the pasteboard handling was in the same process
57 // that called the dragImage method, this would be created automatically.
58 // Create it explicitly because dragImage is called in the UI process.
59 @interface NSFilePromiseDragSource : NSObject
61 char _unknownFields[256];
63 - (id)initWithSource:(id)dragSource;
64 - (void)setTypes:(NSArray *)types onPasteboard:(NSPasteboard *)pasteboard;
67 @interface WKPasteboardFilePromiseOwner : NSFilePromiseDragSource
70 @interface WKPasteboardOwner : NSObject
72 CachedResourceHandle<CachedImage> _image;
74 - (id)initWithImage:(CachedImage*)image;
79 static PassRefPtr<ShareableBitmap> convertImageToBitmap(NSImage *image)
81 RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(IntSize([image size]), ShareableBitmap::SupportsAlpha);
82 OwnPtr<GraphicsContext> graphicsContext = bitmap->createGraphicsContext();
84 RetainPtr<NSGraphicsContext> savedContext = [NSGraphicsContext currentContext];
86 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:graphicsContext->platformContext() flipped:YES]];
87 [image drawInRect:NSMakeRect(0, 0, bitmap->size().width(), bitmap->size().height()) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1 respectFlipped:YES hints:nil];
89 [NSGraphicsContext setCurrentContext:savedContext.get()];
91 return bitmap.release();
94 void WebDragClient::startDrag(RetainPtr<NSImage> image, const IntPoint& point, const IntPoint&, Clipboard*, Frame* frame, bool linkDrag)
96 RefPtr<ShareableBitmap> bitmap = convertImageToBitmap(image.get());
97 ShareableBitmap::Handle handle;
98 if (!bitmap->createHandle(handle))
101 // FIXME: Seems this message should be named StartDrag, not SetDragImage.
102 m_page->send(Messages::WebPageProxy::SetDragImage(frame->view()->contentsToWindow(point), handle, linkDrag));
105 static CachedImage* cachedImage(Element* element)
107 RenderObject* renderer = element->renderer();
110 if (!renderer->isRenderImage())
112 CachedImage* image = toRenderImage(renderer)->cachedImage();
113 if (!image || image->errorOccurred())
118 static NSArray *arrayForURLsWithTitles(NSURL *URL, NSString *title)
120 return [NSArray arrayWithObjects:[NSArray arrayWithObject:[URL _web_originalDataAsString]],
121 [NSArray arrayWithObject:[title _webkit_stringByTrimmingWhitespace]], nil];
124 void WebDragClient::declareAndWriteDragImage(NSPasteboard *pasteboard, DOMElement *element, NSURL *URL, NSString *title, WebCore::Frame*)
127 ASSERT(pasteboard && pasteboard == [NSPasteboard pasteboardWithName:NSDragPboard]);
129 Element* coreElement = core(element);
131 CachedImage* image = cachedImage(coreElement);
133 NSString *extension = @"";
135 extension = image->image()->filenameExtension();
136 if (![extension length])
140 if (![title length]) {
141 title = [[URL path] lastPathComponent];
143 title = [URL _web_userVisibleString];
146 RefPtr<LegacyWebArchive> archive = LegacyWebArchive::create(coreElement);
148 RetainPtr<NSMutableArray> types(AdoptNS, [[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil]);
149 [types.get() addObjectsFromArray:archive ? PasteboardTypes::forImagesWithArchive() : PasteboardTypes::forImages()];
151 m_pasteboardOwner.adoptNS([[WKPasteboardOwner alloc] initWithImage:image]);
152 m_filePromiseOwner.adoptNS([(WKPasteboardFilePromiseOwner *)[WKPasteboardFilePromiseOwner alloc] initWithSource:m_pasteboardOwner.get()]);
154 [pasteboard declareTypes:types.get() owner:m_pasteboardOwner.leakRef()];
156 [pasteboard setPropertyList:[NSArray arrayWithObject:extension] forType:NSFilesPromisePboardType];
158 [m_filePromiseOwner.get() setTypes:[pasteboard propertyListForType:NSFilesPromisePboardType] onPasteboard:pasteboard];
160 [URL writeToPasteboard:pasteboard];
162 [pasteboard setString:[URL _web_originalDataAsString] forType:PasteboardTypes::WebURLPboardType];
164 [pasteboard setString:title forType:PasteboardTypes::WebURLNamePboardType];
166 [pasteboard setString:[URL _web_userVisibleString] forType:NSStringPboardType];
168 [pasteboard setPropertyList:arrayForURLsWithTitles(URL, title) forType:PasteboardTypes::WebURLsWithTitlesPboardType];
171 [pasteboard setData:(NSData *)archive->rawDataRepresentation().get() forType:PasteboardTypes::WebArchivePboardType];
174 void WebDragClient::dragEnded()
176 // The draggedImage method releases its responder; we must retain the WKPasteboardFilePromiseOwner an extra time to balance the release
177 // inside of the function.
178 [m_filePromiseOwner.get() retain];
180 // The drag source we care about here is NSFilePromiseDragSource, which doesn't look at
181 // the arguments. It's OK to just pass arbitrary constant values, so we just pass all zeroes.
182 [m_filePromiseOwner.get() draggedImage:nil endedAt:NSZeroPoint operation:NSDragOperationNone];
184 m_pasteboardOwner = nullptr;
185 m_filePromiseOwner = nullptr;
188 } // namespace WebKit
190 @implementation WKPasteboardFilePromiseOwner
192 // The AppKit implementation of copyDropDirectory gets the current pasteboard in
193 // a way that only works in the process where the drag is initiated. We supply
194 // an implementation that gets the pasteboard by name instead.
195 - (CFURLRef)copyDropDirectory
197 PasteboardRef pasteboard;
198 OSStatus status = PasteboardCreate((CFStringRef)NSDragPboard, &pasteboard);
199 if (status != noErr || !pasteboard)
201 CFURLRef location = 0;
202 status = PasteboardCopyPasteLocation(pasteboard, &location);
203 CFRelease(pasteboard);
204 if (status != noErr || !location)
206 CFMakeCollectable(location);
212 @implementation WKPasteboardOwner
214 static CachedResourceClient* promisedDataClient()
216 static CachedResourceClient* client = new CachedResourceClient;
224 _image->removeClient(promisedDataClient());
228 - (id)initWithImage:(CachedImage*)image
236 image->addClient(promisedDataClient());
252 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
254 if ([type isEqual:NSTIFFPboardType]) {
256 if (Image* image = _image->image())
257 [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
262 // FIXME: Handle RTFD here.
265 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
268 CFRelease(self); // Balanced by the leakRef that WebDragClient::declareAndWriteDragImage does when making this pasteboard owner.
271 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension)
273 NSString *extensionAsSuffix = [@"." stringByAppendingString:extension];
274 return [filename _webkit_hasCaseInsensitiveSuffix:extensionAsSuffix]
275 || ([extension _webkit_isCaseInsensitiveEqualToString:@"jpeg"]
276 && [filename _webkit_hasCaseInsensitiveSuffix:@".jpg"]);
279 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
281 NSFileWrapper *wrapper = nil;
282 NSURL *draggingImageURL = nil;
285 if (SharedBuffer* buffer = _image->CachedResource::data()) {
286 NSData *data = buffer->createNSData();
287 NSURLResponse *response = _image->response().nsURLResponse();
288 draggingImageURL = [response URL];
289 wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
290 NSString* filename = [response suggestedFilename];
291 NSString* trueExtension(_image->image()->filenameExtension());
292 if (!matchesExtensionOrEquivalent(filename, trueExtension))
293 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:trueExtension];
294 [wrapper setPreferredFilename:filename];
298 // FIXME: Do we need to handle the case where we do not have a CachedImage?
299 // WebKit1 had code for this case.
302 LOG_ERROR("Failed to create image file.");
306 // FIXME: Report an error if we fail to create a file.
307 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
308 path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
309 if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
310 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:] at path %@", path);
312 if (draggingImageURL)
313 [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path];
315 return [NSArray arrayWithObject:[path lastPathComponent]];