Fixed: <rdar://problem/3937663> repro assertion failure and crash dragging image...
[WebKit-https.git] / WebKit / WebView.subproj / WebImageView.m
1 /*      
2     WebImageView.m
3     Copyright 2002, Apple, Inc. All rights reserved.
4 */
5
6 #import <WebKit/WebImageView.h>
7
8 #import <WebKit/WebAssertions.h>
9 #import <WebKit/WebDataSource.h>
10 #import <WebKit/WebDocument.h>
11 #import <WebKit/WebFrameView.h>
12 #import <WebKit/WebImageRenderer.h>
13 #import <WebKit/WebImageRendererFactory.h>
14 #import <WebKit/WebImageRepresentation.h>
15 #import <WebKit/WebNSObjectExtras.h>
16 #import <WebKit/WebNSPasteboardExtras.h>
17 #import <WebKit/WebNSViewExtras.h>
18 #import <WebKit/WebViewPrivate.h>
19 #import <WebKit/WebUIDelegatePrivate.h>
20
21 #import <WebCore/WebCoreImageRenderer.h>
22
23 #import <Foundation/NSFileManager_NSURLExtras.h>
24
25 @implementation WebImageView
26
27 + (void)initialize
28 {
29     [NSApp registerServicesMenuSendTypes:[NSArray arrayWithObject:NSTIFFPboardType] returnTypes:nil];
30 }
31
32 + (NSArray *)supportedImageMIMETypes
33 {
34     static NSMutableArray *imageMIMETypes = nil;
35     if (imageMIMETypes == nil) {
36         imageMIMETypes = [[[WebImageRendererFactory sharedFactory] supportedMIMETypes] mutableCopy];
37         [imageMIMETypes removeObject:@"application/pdf"];
38         [imageMIMETypes removeObject:@"application/postscript"];
39     }
40     return imageMIMETypes;
41 }
42
43 - (id)initWithFrame:(NSRect)frame
44 {
45     self = [super initWithFrame:frame];
46     [self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
47     return self;
48 }
49
50 - (void)dealloc
51 {
52     [[rep image] stopAnimation];
53     [rep release];
54     [mouseDownEvent release];
55     
56     [super dealloc];
57 }
58
59 - (void)finalize
60 {
61     [[rep image] stopAnimation];
62     [super finalize];
63 }
64
65 - (BOOL)haveCompleteImage
66 {
67     NSSize imageSize = [[rep image] size];
68     return [rep doneLoading] && imageSize.width > 0 && imageSize.width > 0;
69 }
70
71 - (BOOL)isFlipped 
72 {
73     return YES;
74 }
75
76 - (BOOL)acceptsFirstResponder
77 {
78     // Being first responder is useful for scrolling from the keyboard at least.
79     return YES;
80 }
81
82 - (NSRect)drawingRect
83 {
84     NSSize imageSize = [[rep image] size];
85     return NSMakeRect(0, 0, imageSize.width, imageSize.height);
86 }
87
88 - (void)drawRect:(NSRect)rect
89 {
90     if (needsLayout) {
91         [self layout];
92     }
93     
94     [[NSColor whiteColor] set];
95     NSRectFill(rect);
96     
97     NSRect drawingRect = [self drawingRect];
98     [[rep image] drawImageInRect:drawingRect fromRect:drawingRect];
99 }
100
101 - (void)adjustFrameSize
102 {
103     NSSize size = [[rep image] size];
104     
105     // When drawing on screen, ensure that the view always fills the content area 
106     // (so we draw over the entire previous page), and that the view is at least 
107     // as large as the image.. Otherwise we're printing, and we want the image to 
108     // fill the view so that the printed size doesn't depend on the window size.
109     if ([NSGraphicsContext currentContextDrawingToScreen]) {
110         NSSize clipViewSize = [[self _web_superviewOfClass:[NSClipView class]] frame].size;
111         size.width = MAX(size.width, clipViewSize.width);
112         size.height = MAX(size.height, clipViewSize.height);
113     }
114     
115     [super setFrameSize:size];
116 }
117
118 - (void)setFrameSize:(NSSize)size
119 {
120     [self adjustFrameSize];
121 }
122
123 - (void)layout
124 {
125     [self adjustFrameSize];    
126     needsLayout = NO;
127 }
128
129 - (void)setDataSource:(WebDataSource *)dataSource
130 {
131     ASSERT(!rep);
132     rep = [[dataSource representation] retain];
133 }
134
135 - (void)dataSourceUpdated:(WebDataSource *)dataSource
136 {
137     NSSize imageSize = [[rep image] size];
138     if (imageSize.width > 0 && imageSize.height > 0) {
139         [self setNeedsLayout:YES];
140         [self setNeedsDisplay:YES];
141     }
142 }
143
144 - (void)setNeedsLayout: (BOOL)flag
145 {
146     needsLayout = flag;
147 }
148
149 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
150 {
151 }
152
153 - (void)viewDidMoveToHostWindow
154 {
155 }
156
157 - (void)viewDidMoveToWindow
158 {
159     if (![self window]){
160         [[rep image] stopAnimation];
161     }
162     
163     [super viewDidMoveToWindow];
164 }
165
166 - (WebView *)webView
167 {
168     return [self _web_parentWebView];
169 }
170
171 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
172 {
173     if ([item action] == @selector(copy:)){
174         return [self haveCompleteImage];
175     }
176
177     return YES;
178 }
179
180 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
181 {
182     if (sendType && [sendType isEqualToString:NSTIFFPboardType]){
183         return self;
184     }
185
186     return [super validRequestorForSendType:sendType returnType:returnType];
187 }
188
189 - (BOOL)writeImageToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
190 {    
191     if ([self haveCompleteImage]) {
192         [pasteboard _web_writeImage:[rep image] URL:[rep URL] title:nil archive:[rep archive] types:types];
193         return YES;
194     }
195     
196     return NO;
197 }
198
199 - (void)copy:(id)sender
200 {
201     [self writeImageToPasteboard:[NSPasteboard generalPasteboard] types:[NSPasteboard _web_writableTypesForImage]];
202 }
203
204 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
205 {
206     return [self writeImageToPasteboard:pasteboard types:types];
207 }
208
209 - (NSDictionary *)elementAtPoint:(NSPoint)point
210 {
211     WebFrame *frame = [[self _web_parentWebFrameView] webFrame];
212     ASSERT(frame);
213     
214     return [NSDictionary dictionaryWithObjectsAndKeys:
215         [rep image],                            WebElementImageKey,
216         [NSValue valueWithRect:[self bounds]],  WebElementImageRectKey,
217         [rep URL],                              WebElementImageURLKey,
218         [NSNumber numberWithBool:NO],           WebElementIsSelectedKey,
219         frame,                                  WebElementFrameKey, nil];
220 }
221
222 - (NSMenu *)menuForEvent:(NSEvent *)theEvent
223 {
224     WebView *webView = [self webView];
225     ASSERT(webView);
226     return [webView _menuForElement:[self elementAtPoint:NSZeroPoint]];
227 }
228
229 - (void)mouseDown:(NSEvent *)event
230 {
231     ignoringMouseDraggedEvents = NO;
232     [mouseDownEvent release];
233     mouseDownEvent = [event retain];
234     
235     WebView *webView = [self webView];
236     NSPoint point = [webView convertPoint:[mouseDownEvent locationInWindow] fromView:nil];
237     dragSourceActionMask = [[webView _UIDelegateForwarder] webView:webView dragSourceActionMaskForPoint:point];
238     
239     [super mouseDown:event];
240 }
241
242 - (void)mouseDragged:(NSEvent *)mouseDraggedEvent
243 {
244     if (ignoringMouseDraggedEvents || ![self haveCompleteImage] || !(dragSourceActionMask & WebDragSourceActionImage)) {
245         return;
246     }
247     
248     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
249     id source = [pasteboard _web_declareAndWriteDragImage:[rep image]
250                                                       URL:[rep URL]
251                                                     title:nil
252                                                   archive:[rep archive]
253                                                    source:self];
254     
255     WebView *webView = [self webView];
256     NSPoint point = [webView convertPoint:[mouseDownEvent locationInWindow] fromView:nil];
257     [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionImage fromPoint:point withPasteboard:pasteboard];
258     
259     [[self webView] _setInitiatedDrag:YES];
260     
261     // Retain this view during the drag because it may be released before the drag ends.
262     [self retain];
263     
264     [self _web_dragImage:[rep image]
265                     rect:[self drawingRect]
266                    event:mouseDraggedEvent
267               pasteboard:pasteboard
268                   source:source
269                   offset:NULL];
270 }
271
272 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
273 {
274     // FIXME: Report an error if we fail to create a file.
275     NSString *path = [[dropDestination path] stringByAppendingPathComponent:[rep filename]];
276     path = [[NSFileManager defaultManager] _web_pathWithUniqueFilenameForPath:path];
277     [[rep data] writeToFile:path atomically:NO];
278     return [NSArray arrayWithObject:[path lastPathComponent]];
279 }
280
281 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
282 {
283     // Prevent queued mouseDragged events from coming after the drag which can cause a double drag.
284     ignoringMouseDraggedEvents = YES;
285     
286     [[self webView] _setInitiatedDrag:NO];
287
288     // Balance the previous retain from when the drag started.
289     [self release];
290 }
291
292 - (NSImage *)image
293 {
294     return [[rep image] image];
295 }
296
297 #pragma mark PRINTING
298
299 - (void)drawPageBorderWithSize:(NSSize)borderSize
300 {
301     ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));
302     // FIXME: How to determine the number of pages required to print the whole image?
303     [[self webView] _drawHeaderAndFooter];
304 }
305
306 - (void)beginDocument
307 {
308     [self adjustFrameSize];
309     [[self webView] _adjustPrintingMarginsForHeaderAndFooter];
310     [super beginDocument];
311 }
312
313 - (void)endDocument
314 {
315     [super endDocument];
316     [self adjustFrameSize];
317 }
318
319 @end