[iOS] Upstream PLATFORM(IOS) changes to Source/WebKit/
[WebKit-https.git] / Source / WebKit / mac / Misc / WebNSViewExtras.m
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, 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 <WebKit/WebNSViewExtras.h>
30
31 #import <WebKit/DOMExtensions.h>
32 #import <WebKit/WebDataSource.h>
33 #import <WebKit/WebFramePrivate.h>
34 #import <WebKit/WebFrameViewInternal.h>
35 #import <WebKit/WebNSImageExtras.h>
36 #import <WebKit/WebNSPasteboardExtras.h>
37 #import <WebKit/WebNSURLExtras.h>
38 #import <WebKit/WebView.h>
39
40 #if PLATFORM(IOS)
41 #import <WebCore/WAKViewPrivate.h>
42 #import <WebCore/WAKWindow.h>
43 #endif
44
45 #define WebDragStartHysteresisX                 5.0f
46 #define WebDragStartHysteresisY                 5.0f
47 #define WebMaxDragImageSize                     NSMakeSize(400.0f, 400.0f)
48 #define WebMaxOriginalImageArea                 (1500.0f * 1500.0f)
49 #define WebDragIconRightInset                   7.0f
50 #define WebDragIconBottomInset                  3.0f
51
52 @implementation NSView (WebExtras)
53
54 - (NSView *)_web_superviewOfClass:(Class)class
55 {
56     NSView *view = [self superview];
57     while (view  && ![view isKindOfClass:class])
58         view = [view superview];
59     return view;
60 }
61
62 - (WebFrameView *)_web_parentWebFrameView
63 {
64     return (WebFrameView *)[self _web_superviewOfClass:[WebFrameView class]];
65 }
66
67 #if !PLATFORM(IOS)
68 // FIXME: Mail is the only client of _webView, remove this method once no versions of Mail need it.
69 - (WebView *)_webView
70 {
71     return (WebView *)[self _web_superviewOfClass:[WebView class]];
72 }
73
74 /* Determine whether a mouse down should turn into a drag; started as copy of NSTableView code */
75 - (BOOL)_web_dragShouldBeginFromMouseDown:(NSEvent *)mouseDownEvent
76                            withExpiration:(NSDate *)expiration
77                               xHysteresis:(float)xHysteresis
78                               yHysteresis:(float)yHysteresis
79 {
80     NSEvent *nextEvent, *firstEvent, *dragEvent, *mouseUp;
81     BOOL dragIt;
82
83     if ([mouseDownEvent type] != NSLeftMouseDown) {
84         return NO;
85     }
86
87     nextEvent = nil;
88     firstEvent = nil;
89     dragEvent = nil;
90     mouseUp = nil;
91     dragIt = NO;
92
93     while ((nextEvent = [[self window] nextEventMatchingMask:(NSLeftMouseUpMask | NSLeftMouseDraggedMask)
94                                                    untilDate:expiration
95                                                       inMode:NSEventTrackingRunLoopMode
96                                                      dequeue:YES]) != nil) {
97         if (firstEvent == nil) {
98             firstEvent = nextEvent;
99         }
100
101         if ([nextEvent type] == NSLeftMouseDragged) {
102             float deltax = ABS([nextEvent locationInWindow].x - [mouseDownEvent locationInWindow].x);
103             float deltay = ABS([nextEvent locationInWindow].y - [mouseDownEvent locationInWindow].y);
104             dragEvent = nextEvent;
105
106             if (deltax >= xHysteresis) {
107                 dragIt = YES;
108                 break;
109             }
110
111             if (deltay >= yHysteresis) {
112                 dragIt = YES;
113                 break;
114             }
115         } else if ([nextEvent type] == NSLeftMouseUp) {
116             mouseUp = nextEvent;
117             break;
118         }
119     }
120
121     // Since we've been dequeuing the events (If we don't, we'll never see the mouse up...),
122     // we need to push some of the events back on.  It makes sense to put the first and last
123     // drag events and the mouse up if there was one.
124     if (mouseUp != nil) {
125         [NSApp postEvent:mouseUp atStart:YES];
126     }
127     if (dragEvent != nil) {
128         [NSApp postEvent:dragEvent atStart:YES];
129     }
130     if (firstEvent != mouseUp && firstEvent != dragEvent) {
131         [NSApp postEvent:firstEvent atStart:YES];
132     }
133
134     return dragIt;
135 }
136
137 - (BOOL)_web_dragShouldBeginFromMouseDown:(NSEvent *)mouseDownEvent
138                            withExpiration:(NSDate *)expiration
139 {
140     return [self _web_dragShouldBeginFromMouseDown:mouseDownEvent
141                                     withExpiration:expiration
142                                        xHysteresis:WebDragStartHysteresisX
143                                        yHysteresis:WebDragStartHysteresisY];
144 }
145
146
147 - (NSDragOperation)_web_dragOperationForDraggingInfo:(id <NSDraggingInfo>)sender
148 {
149     if (![NSApp modalWindow] && 
150         ![[self window] attachedSheet] &&
151         [sender draggingSource] != self &&
152         [[sender draggingPasteboard] _web_bestURL]) {
153
154         return NSDragOperationCopy;
155     }
156     
157     return NSDragOperationNone;
158 }
159
160 - (void)_web_DragImageForElement:(DOMElement *)element
161                          rect:(NSRect)rect
162                         event:(NSEvent *)event
163                    pasteboard:(NSPasteboard *)pasteboard 
164                        source:(id)source
165                        offset:(NSPoint *)dragImageOffset
166 {
167     NSPoint mouseDownPoint = [self convertPoint:[event locationInWindow] fromView:nil];
168     NSImage *dragImage;
169     NSPoint origin;
170
171     NSImage *image = [element image];
172     if (image != nil && [image size].height * [image size].width <= WebMaxOriginalImageArea) {
173         NSSize originalSize = rect.size;
174         origin = rect.origin;
175         
176         dragImage = [[image copy] autorelease];
177 #pragma clang diagnostic push
178 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
179         [dragImage setScalesWhenResized:YES];
180 #pragma clang diagnostic pop
181         [dragImage setSize:originalSize];
182         
183         [dragImage _web_scaleToMaxSize:WebMaxDragImageSize];
184         NSSize newSize = [dragImage size];
185         
186         [dragImage _web_dissolveToFraction:WebDragImageAlpha];
187         
188         // Properly orient the drag image and orient it differently if it's smaller than the original
189         origin.x = mouseDownPoint.x - (((mouseDownPoint.x - origin.x) / originalSize.width) * newSize.width);
190         origin.y = origin.y + originalSize.height;
191         origin.y = mouseDownPoint.y - (((mouseDownPoint.y - origin.y) / originalSize.height) * newSize.height);
192     } else {
193         // FIXME: This has been broken for a while.
194         // There's no way to get the MIME type for the image from a DOM element.
195         // The old code used WKGetPreferredExtensionForMIMEType([image MIMEType]);
196         NSString *extension = @"";
197         dragImage = [[NSWorkspace sharedWorkspace] iconForFileType:extension];
198         NSSize offset = NSMakeSize([dragImage size].width - WebDragIconRightInset, -WebDragIconBottomInset);
199         origin = NSMakePoint(mouseDownPoint.x - offset.width, mouseDownPoint.y - offset.height);
200     }
201
202     // This is the offset from the lower left corner of the image to the mouse location.  Because we
203     // are a flipped view the calculation of Y is inverted.
204     if (dragImageOffset) {
205         dragImageOffset->x = mouseDownPoint.x - origin.x;
206         dragImageOffset->y = origin.y - mouseDownPoint.y;
207     }
208     
209     // Per kwebster, offset arg is ignored
210 #pragma clang diagnostic push
211 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
212     [self dragImage:dragImage at:origin offset:NSZeroSize event:event pasteboard:pasteboard source:source slideBack:YES];
213 #pragma clang diagnostic pop
214 }
215 #endif // !PLATFORM(IOS)
216
217 - (BOOL)_web_firstResponderIsSelfOrDescendantView
218 {
219     NSResponder *responder = [[self window] firstResponder];
220     return (responder && 
221            (responder == self || 
222            ([responder isKindOfClass:[NSView class]] && [(NSView *)responder isDescendantOf:self])));
223 }
224
225 - (NSRect)_web_convertRect:(NSRect)aRect toView:(NSView *)aView
226 {
227     // Converting to this view's window; let -convertRect:toView: handle it
228     if (aView == nil)
229         return [self convertRect:aRect toView:nil];
230         
231     // This view must be in a window.  Do whatever weird thing -convertRect:toView: does in this situation.
232     NSWindow *thisWindow = [self window];
233     if (!thisWindow)
234         return [self convertRect:aRect toView:aView];
235     
236     // The other view must be in a window, too.
237     NSWindow *otherWindow = [aView window];
238     if (!otherWindow)
239         return [self convertRect:aRect toView:aView];
240
241     // Convert to this window's coordinates
242     NSRect convertedRect = [self convertRect:aRect toView:nil];
243     
244     // Convert to screen coordinates
245 #pragma clang diagnostic push
246 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
247     convertedRect.origin = [thisWindow convertBaseToScreen:convertedRect.origin];
248
249     // Convert to other window's coordinates
250     convertedRect.origin = [otherWindow convertScreenToBase:convertedRect.origin];
251 #pragma clang diagnostic pop
252     
253     // Convert to other view's coordinates
254     convertedRect = [aView convertRect:convertedRect fromView:nil];
255     
256     return convertedRect;
257 }
258
259 @end
260
261 #if PLATFORM(IOS)
262 @implementation NSView (WebDocumentViewExtras)
263
264 - (WebFrame *)_frame
265 {
266     WebFrameView *webFrameView = [self _web_parentWebFrameView];
267     return [webFrameView webFrame];
268 }
269
270 - (WebView *)_webView
271 {
272     // We used to use the view hierarchy exclusively here, but that won't work
273     // right when the first viewDidMoveToSuperview call is done, and this will.
274     return [[self _frame] webView];
275 }
276
277 @end
278 #endif