1/*
2 * Copyright (C) 2005, 2006, 2007 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 Computer, 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#import "WebNSPasteboardExtras.h"
30
31#import "DOMElementInternal.h"
32#import "WebArchive.h"
33#import "WebFrameInternal.h"
34#import "WebHTMLViewInternal.h"
35#import "WebNSURLExtras.h"
36#import "WebResourcePrivate.h"
37#import "WebURLsWithTitles.h"
38#import "WebViewPrivate.h"
39#import <WebCore/Element.h>
40#import <WebCore/Image.h>
41#import <WebCore/MIMETypeRegistry.h>
42#import <WebCore/RenderImage.h>
43#import <WebKit/DOMExtensions.h>
44#import <WebKit/DOMPrivate.h>
45#import <WebKitSystemInterface.h>
46#import <wtf/Assertions.h>
47#import <wtf/RetainPtr.h>
48#import <wtf/StdLibExtras.h>
49
50using namespace WebCore;
51
52NSString *WebURLPboardType = @"public.url";
53NSString *WebURLNamePboardType = @"public.url-name";
54
55@implementation NSPasteboard (WebExtras)
56
57+ (NSArray *)_web_writableTypesForURL
58{
59    DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, types, ([[NSArray alloc] initWithObjects:
60        WebURLsWithTitlesPboardType,
61        NSURLPboardType,
62        WebURLPboardType,
63        WebURLNamePboardType,
64        NSStringPboardType,
65        nil]));
66    return types.get();
67}
68
69static inline NSArray *_createWritableTypesForImageWithoutArchive()
70{
71    NSMutableArray *types = [[NSMutableArray alloc] initWithObjects:NSTIFFPboardType, nil];
72    [types addObjectsFromArray:[NSPasteboard _web_writableTypesForURL]];
73    return types;
74}
75
76static NSArray *_writableTypesForImageWithoutArchive (void)
77{
78    DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, types, (_createWritableTypesForImageWithoutArchive()));
79    return types.get();
80}
81
82static inline NSArray *_createWritableTypesForImageWithArchive()
83{
84    NSMutableArray *types = [_writableTypesForImageWithoutArchive() mutableCopy];
85    [types addObject:NSRTFDPboardType];
86    [types addObject:WebArchivePboardType];
87    return types;
88}
89
90static NSArray *_writableTypesForImageWithArchive (void)
91{
92    DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, types, (_createWritableTypesForImageWithArchive()));
93    return types.get();
94}
95
96+ (NSArray *)_web_writableTypesForImageIncludingArchive:(BOOL)hasArchive
97{
98    return hasArchive
99        ? _writableTypesForImageWithArchive()
100        : _writableTypesForImageWithoutArchive();
101}
102
103+ (NSArray *)_web_dragTypesForURL
104{
105    return [NSArray arrayWithObjects:
106        WebURLsWithTitlesPboardType,
107        NSURLPboardType,
108        WebURLPboardType,
109        WebURLNamePboardType,
110        NSStringPboardType,
111        NSFilenamesPboardType,
112        nil];
113}
114
115- (NSURL *)_web_bestURL
116{
117    NSArray *types = [self types];
118
119    if ([types containsObject:NSURLPboardType]) {
120        NSURL *URLFromPasteboard = [NSURL URLFromPasteboard:self];
121        NSString *scheme = [URLFromPasteboard scheme];
122        if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"]) {
123            return [URLFromPasteboard _webkit_canonicalize];
124        }
125    }
126
127    if ([types containsObject:NSStringPboardType]) {
128        NSString *URLString = [self stringForType:NSStringPboardType];
129        if ([URLString _webkit_looksLikeAbsoluteURL]) {
130            NSURL *URL = [[NSURL _web_URLWithUserTypedString:URLString] _webkit_canonicalize];
131            if (URL) {
132                return URL;
133            }
134        }
135    }
136
137    if ([types containsObject:NSFilenamesPboardType]) {
138        NSArray *files = [self propertyListForType:NSFilenamesPboardType];
139        // FIXME: Maybe it makes more sense to allow multiple files and only use the first one?
140        if ([files count] == 1) {
141            NSString *file = [files objectAtIndex:0];
142            // FIXME: We are filtering out directories because that's what the original code used to
143            // do. Without this check, if the URL points to a local directory, Safari will open the
144            // parent directory of the directory in Finder. This check should go away as soon as
145            // possible.
146            BOOL isDirectory;
147            if ([[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory] && isDirectory)
148                return nil;
149            return [[NSURL fileURLWithPath:file] _webkit_canonicalize];
150        }
151    }
152
153    return nil;
154}
155
156- (void)_web_writeURL:(NSURL *)URL andTitle:(NSString *)title types:(NSArray *)types
157{
158    ASSERT(URL);
159
160    if ([title length] == 0) {
161        title = [[URL path] lastPathComponent];
162        if ([title length] == 0)
163            title = [URL _web_userVisibleString];
164    }
165
166    if ([types containsObject:NSURLPboardType])
167        [URL writeToPasteboard:self];
168    if ([types containsObject:WebURLPboardType])
169        [self setString:[URL _web_originalDataAsString] forType:WebURLPboardType];
170    if ([types containsObject:WebURLNamePboardType])
171        [self setString:title forType:WebURLNamePboardType];
172    if ([types containsObject:NSStringPboardType])
173        [self setString:[URL _web_userVisibleString] forType:NSStringPboardType];
174    if ([types containsObject:WebURLsWithTitlesPboardType])
175        [WebURLsWithTitles writeURLs:[NSArray arrayWithObject:URL] andTitles:[NSArray arrayWithObject:title] toPasteboard:self];
176}
177
178+ (int)_web_setFindPasteboardString:(NSString *)string withOwner:(id)owner
179{
180    NSPasteboard *findPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard];
181    [findPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:owner];
182    [findPasteboard setString:string forType:NSStringPboardType];
183    return [findPasteboard changeCount];
184}
185
186- (void)_web_writeFileWrapperAsRTFDAttachment:(NSFileWrapper *)wrapper
187{
188    NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:wrapper];
189
190    NSAttributedString *string = [NSAttributedString attributedStringWithAttachment:attachment];
191    [attachment release];
192
193    NSData *RTFDData = [string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
194    [self setData:RTFDData forType:NSRTFDPboardType];
195}
196
197
198- (void)_web_writePromisedRTFDFromArchive:(WebArchive*)archive containsImage:(BOOL)containsImage
199{
200    ASSERT(archive);
201    // This image data is either the only subresource of an archive (HTML image case)
202    // or the main resource (standalone image case).
203    NSArray *subresources = [archive subresources];
204    WebResource *resource = [archive mainResource];
205    if (containsImage && [subresources count] > 0
206        && MIMETypeRegistry::isSupportedImageResourceMIMEType([[subresources objectAtIndex:0] MIMEType]))
207        resource = (WebResource *)[subresources objectAtIndex:0];
208    ASSERT(resource != nil);
209
210    ASSERT(!containsImage || MIMETypeRegistry::isSupportedImageResourceMIMEType([resource MIMEType]));
211    if (!containsImage || MIMETypeRegistry::isSupportedImageResourceMIMEType([resource MIMEType]))
212        [self _web_writeFileWrapperAsRTFDAttachment:[resource _fileWrapperRepresentation]];
213
214}
215
216static CachedImage* imageFromElement(DOMElement *domElement)
217{
218    Element* element = core(domElement);
219    if (!element)
220        return 0;
221
222    RenderObject* renderer = element->renderer();
223    RenderImage* imageRenderer = toRenderImage(renderer);
224    if (!imageRenderer->cachedImage() || imageRenderer->cachedImage()->errorOccurred())
225        return 0;
226    return imageRenderer->cachedImage();
227}
228
229- (void)_web_writeImage:(NSImage *)image
230                element:(DOMElement *)element
231                    URL:(NSURL *)URL
232                  title:(NSString *)title
233                archive:(WebArchive *)archive
234                  types:(NSArray *)types
235                 source:(WebHTMLView *)source
236{
237    ASSERT(image || element);
238    ASSERT(URL);
239
240    [self _web_writeURL:URL andTitle:title types:types];
241
242    if ([types containsObject:NSTIFFPboardType]) {
243        if (image)
244            [self setData:[image TIFFRepresentation] forType:NSTIFFPboardType];
245        else if (source && element)
246            [source setPromisedDragTIFFDataSource:imageFromElement(element)];
247        else if (element)
248            [self setData:[element _imageTIFFRepresentation] forType:NSTIFFPboardType];
249    }
250
251    if (archive)
252        if ([types containsObject:WebArchivePboardType])
253            [self setData:[archive data] forType:WebArchivePboardType];
254    else {
255        // We should not have declared types that we aren't going to write (4031826).
256        ASSERT(![types containsObject:NSRTFDPboardType]);
257        ASSERT(![types containsObject:WebArchivePboardType]);
258    }
259}
260
261- (id)_web_declareAndWriteDragImageForElement:(DOMElement *)element
262                                       URL:(NSURL *)URL
263                                     title:(NSString *)title
264                                   archive:(WebArchive *)archive
265                                    source:(WebHTMLView *)source
266{
267    ASSERT(self == [NSPasteboard pasteboardWithName:NSDragPboard]);
268
269    NSString *extension = @"";
270    if (RenderObject* renderer = core(element)->renderer()) {
271        if (renderer->isImage()) {
272            if (CachedImage* image = toRenderImage(renderer)->cachedImage()) {
273                extension = image->image()->filenameExtension();
274                if (![extension length])
275                    return 0;
276            }
277        }
278    }
279
280    NSMutableArray *types = [[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil];
281    [types addObjectsFromArray:[NSPasteboard _web_writableTypesForImageIncludingArchive:(archive != nil)]];
282    [self declareTypes:types owner:source];
283    [self _web_writeImage:nil element:element URL:URL title:title archive:archive types:types source:source];
284    [types release];
285
286    NSArray *extensions = [[NSArray alloc] initWithObjects:extension, nil];
287    [self setPropertyList:extensions forType:NSFilesPromisePboardType];
288    [extensions release];
289
290    return source;
291}
292
293@end
294