Source/WebCore: WK2: Cannot set focus on an element when focus is outside of WKView
[WebKit-https.git] / Source / WebCore / platform / mac / WidgetMac.mm
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2008, 2010, 2011 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  * 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.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "Widget.h"
28
29 #ifdef BUILDING_ON_TIGER
30 #import "AutodrainedPool.h"
31 #endif
32
33 #import "BlockExceptions.h"
34 #import "Chrome.h"
35 #import "Cursor.h"
36 #import "Document.h"
37 #import "FloatConversion.h"
38 #import "Font.h"
39 #import "Frame.h"
40 #import "GraphicsContext.h"
41 #import "NotImplemented.h"
42 #import "Page.h"
43 #import "PlatformMouseEvent.h"
44 #import "ScrollView.h"
45 #import "WebCoreFrameView.h"
46 #import "WebCoreView.h"
47 #import <wtf/RetainPtr.h>
48
49 @interface NSWindow (WebWindowDetails)
50 - (BOOL)_needsToResetDragMargins;
51 - (void)_setNeedsToResetDragMargins:(BOOL)needs;
52 @end
53
54 @interface NSView (WebSetSelectedMethods)
55 - (void)setIsSelected:(BOOL)isSelected;
56 - (void)webPlugInSetIsSelected:(BOOL)isSelected;
57 @end
58
59 @interface NSView (Widget)
60 - (void)visibleRectDidChange;
61 @end
62
63 namespace WebCore {
64
65 class WidgetPrivate {
66 public:
67     WidgetPrivate()
68         : previousVisibleRect(NSZeroRect)
69     {
70     }
71
72     bool mustStayInWindow;
73     bool removeFromSuperviewSoon;
74     NSRect previousVisibleRect;
75 };
76
77 static void safeRemoveFromSuperview(NSView *view)
78 {
79     // If the the view is the first responder, then set the window's first responder to nil so
80     // we don't leave the window pointing to a view that's no longer in it.
81     NSWindow *window = [view window];
82     NSResponder *firstResponder = [window firstResponder];
83     if ([firstResponder isKindOfClass:[NSView class]] && [(NSView *)firstResponder isDescendantOf:view])
84         [window makeFirstResponder:nil];
85
86     // Suppress the resetting of drag margins since we know we can't affect them.
87     BOOL resetDragMargins = [window _needsToResetDragMargins];
88     [window _setNeedsToResetDragMargins:NO];
89     [view removeFromSuperview];
90     [window _setNeedsToResetDragMargins:resetDragMargins];
91 }
92
93 Widget::Widget(NSView *view)
94     : m_data(new WidgetPrivate)
95 {
96     init(view);
97     m_data->mustStayInWindow = false;
98     m_data->removeFromSuperviewSoon = false;
99 }
100
101 Widget::~Widget()
102 {
103     delete m_data;
104 }
105
106 // FIXME: Should move this to Chrome; bad layering that this knows about Frame.
107 void Widget::setFocus(bool focused)
108 {
109     if (!focused)
110         return;
111
112     Frame* frame = Frame::frameForWidget(this);
113     if (!frame)
114         return;
115
116     BEGIN_BLOCK_OBJC_EXCEPTIONS;
117  
118     // If there's no platformWidget, WK2 is running. The focus() method needs to be used
119     // to bring focus to the right view on the UIProcess side.
120     NSView *view = [platformWidget() _webcore_effectiveFirstResponder];
121     if (Page* page = frame->page()) {
122         if (!platformWidget())
123             page->chrome()->focus();
124         else
125             page->chrome()->focusNSView(view);
126     }
127     END_BLOCK_OBJC_EXCEPTIONS;
128 }
129
130 void Widget::setCursor(const Cursor& cursor)
131 {
132     ScrollView* view = root();
133     if (!view)
134         return;
135     view->hostWindow()->setCursor(cursor);
136 }
137
138 void Widget::show()
139 {
140     if (isSelfVisible())
141         return;
142
143     setSelfVisible(true);
144
145     BEGIN_BLOCK_OBJC_EXCEPTIONS;
146     [getOuterView() setHidden:NO];
147     END_BLOCK_OBJC_EXCEPTIONS;
148 }
149
150 void Widget::hide()
151 {
152     if (!isSelfVisible())
153         return;
154
155     setSelfVisible(false);
156
157     BEGIN_BLOCK_OBJC_EXCEPTIONS;
158     [getOuterView() setHidden:YES];
159     END_BLOCK_OBJC_EXCEPTIONS;
160 }
161
162 IntRect Widget::frameRect() const
163 {
164     if (!platformWidget())
165         return m_frame;
166
167     BEGIN_BLOCK_OBJC_EXCEPTIONS;
168     return enclosingIntRect([getOuterView() frame]);
169     END_BLOCK_OBJC_EXCEPTIONS;
170     
171     return m_frame;
172 }
173
174 void Widget::setFrameRect(const IntRect& rect)
175 {
176     m_frame = rect;
177
178     BEGIN_BLOCK_OBJC_EXCEPTIONS;
179     NSView *outerView = getOuterView();
180     if (!outerView)
181         return;
182
183     // Take a reference to this Widget, because sending messages to outerView can invoke arbitrary
184     // code, which can deref it.
185     RefPtr<Widget> protectedThis(this);
186
187     NSRect visibleRect = [outerView visibleRect];
188     NSRect f = rect;
189     if (!NSEqualRects(f, [outerView frame])) {
190         [outerView setFrame:f];
191         [outerView setNeedsDisplay:NO];
192     } else if (!NSEqualRects(visibleRect, m_data->previousVisibleRect) && [outerView respondsToSelector:@selector(visibleRectDidChange)])
193         [outerView visibleRectDidChange];
194
195     m_data->previousVisibleRect = visibleRect;
196     END_BLOCK_OBJC_EXCEPTIONS;
197 }
198
199 void Widget::setBoundsSize(const IntSize& size)
200 {
201     NSSize nsSize = size;
202
203     BEGIN_BLOCK_OBJC_EXCEPTIONS;
204     NSView *outerView = getOuterView();
205     if (!outerView)
206         return;
207
208     // Take a reference to this Widget, because sending messages to outerView can invoke arbitrary
209     // code, which can deref it.
210     RefPtr<Widget> protectedThis(this);
211     if (!NSEqualSizes(nsSize, [outerView bounds].size)) {
212         [outerView setBoundsSize:nsSize];
213         [outerView setNeedsDisplay:NO];
214     }
215     END_BLOCK_OBJC_EXCEPTIONS;
216 }
217
218 NSView *Widget::getOuterView() const
219 {
220     NSView *view = platformWidget();
221
222     // If this widget's view is a WebCoreFrameScrollView then we
223     // resize its containing view, a WebFrameView.
224     if ([view conformsToProtocol:@protocol(WebCoreFrameScrollView)]) {
225         view = [view superview];
226         ASSERT(view);
227     }
228
229     return view;
230 }
231
232 void Widget::paint(GraphicsContext* p, const IntRect& r)
233 {
234     if (p->paintingDisabled())
235         return;
236     NSView *view = getOuterView();
237
238     // Take a reference to this Widget, because sending messages to the views can invoke arbitrary
239     // code, which can deref it.
240     RefPtr<Widget> protectedThis(this);
241
242     IntPoint transformOrigin = frameRect().location();
243     AffineTransform widgetToViewTranform = makeMapBetweenRects(IntRect(IntPoint(), frameRect().size()), [view bounds]);
244
245     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
246     if (currentContext == [[view window] graphicsContext] || ![currentContext isDrawingToScreen]) {
247         // This is the common case of drawing into a window or printing.
248         BEGIN_BLOCK_OBJC_EXCEPTIONS;
249         
250         CGContextRef context = (CGContextRef)[currentContext graphicsPort];
251
252         CGContextSaveGState(context);
253         CGContextTranslateCTM(context, transformOrigin.x(), transformOrigin.y());
254         CGContextScaleCTM(context, narrowPrecisionToFloat(widgetToViewTranform.xScale()), narrowPrecisionToFloat(widgetToViewTranform.yScale()));
255         CGContextTranslateCTM(context, -transformOrigin.x(), -transformOrigin.y());
256
257         IntRect dirtyRect = r;
258         dirtyRect.move(-transformOrigin.x(), -transformOrigin.y());
259         if (![view isFlipped])
260             dirtyRect.setY([view bounds].size.height - dirtyRect.maxY());
261
262         [view displayRectIgnoringOpacity:dirtyRect];
263
264         CGContextRestoreGState(context);
265
266         END_BLOCK_OBJC_EXCEPTIONS;
267     } else {
268         // This is the case of drawing into a bitmap context other than a window backing store. It gets hit beneath
269         // -cacheDisplayInRect:toBitmapImageRep:, and when painting into compositing layers.
270
271         // Transparent subframes are in fact implemented with scroll views that return YES from -drawsBackground (whenever the WebView
272         // itself is in drawsBackground mode). In the normal drawing code path, the scroll views are never asked to draw the background,
273         // so this is not an issue, but in this code path they are, so the following code temporarily turns background drwaing off.
274         NSView *innerView = platformWidget();
275         NSScrollView *scrollView = 0;
276         if ([innerView conformsToProtocol:@protocol(WebCoreFrameScrollView)]) {
277             ASSERT([innerView isKindOfClass:[NSScrollView class]]);
278             NSScrollView *scrollView = static_cast<NSScrollView *>(innerView);
279             // -copiesOnScroll will return NO whenever the content view is not fully opaque.
280             if ([scrollView drawsBackground] && ![[scrollView contentView] copiesOnScroll])
281                 [scrollView setDrawsBackground:NO];
282             else
283                 scrollView = 0;
284         }
285
286         CGContextRef cgContext = p->platformContext();
287         ASSERT(cgContext == [currentContext graphicsPort]);
288         CGContextSaveGState(cgContext);
289
290         CGContextTranslateCTM(cgContext, transformOrigin.x(), transformOrigin.y());
291         CGContextScaleCTM(cgContext, narrowPrecisionToFloat(widgetToViewTranform.xScale()), narrowPrecisionToFloat(widgetToViewTranform.yScale()));
292         CGContextTranslateCTM(cgContext, -transformOrigin.x(), -transformOrigin.y());
293
294         NSRect viewFrame = [view frame];
295         NSRect viewBounds = [view bounds];
296         // Set up the translation and (flipped) orientation of the graphics context. In normal drawing, AppKit does it as it descends down
297         // the view hierarchy.
298         CGContextTranslateCTM(cgContext, viewFrame.origin.x - viewBounds.origin.x, viewFrame.origin.y + viewFrame.size.height + viewBounds.origin.y);
299         CGContextScaleCTM(cgContext, 1, -1);
300
301         IntRect dirtyRect = r;
302         dirtyRect.move(-transformOrigin.x(), -transformOrigin.y());
303         if (![view isFlipped])
304             dirtyRect.setY([view bounds].size.height - dirtyRect.maxY());
305
306         BEGIN_BLOCK_OBJC_EXCEPTIONS;
307         {
308 #ifdef BUILDING_ON_TIGER
309             AutodrainedPool pool;
310 #endif
311             NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES];
312             [view displayRectIgnoringOpacity:dirtyRect inContext:nsContext];
313         }
314         END_BLOCK_OBJC_EXCEPTIONS;
315
316         CGContextRestoreGState(cgContext);
317
318         if (scrollView)
319             [scrollView setDrawsBackground:YES];
320     }
321 }
322
323 void Widget::setIsSelected(bool isSelected)
324 {
325     NSView *view = platformWidget();
326
327     BEGIN_BLOCK_OBJC_EXCEPTIONS;
328     if ([view respondsToSelector:@selector(webPlugInSetIsSelected:)])
329         [view webPlugInSetIsSelected:isSelected];
330     else if ([view respondsToSelector:@selector(setIsSelected:)])
331         [view setIsSelected:isSelected];
332     END_BLOCK_OBJC_EXCEPTIONS;
333 }
334
335 void Widget::removeFromSuperview()
336 {
337     if (m_data->mustStayInWindow)
338         m_data->removeFromSuperviewSoon = true;
339     else {
340         m_data->removeFromSuperviewSoon = false;
341         BEGIN_BLOCK_OBJC_EXCEPTIONS;
342         safeRemoveFromSuperview(getOuterView());
343         END_BLOCK_OBJC_EXCEPTIONS;
344     }
345 }
346
347 void Widget::beforeMouseDown(NSView *unusedView, Widget* widget)
348 {
349     if (widget) {
350         ASSERT_UNUSED(unusedView, unusedView == widget->getOuterView());
351         ASSERT(!widget->m_data->mustStayInWindow);
352         widget->m_data->mustStayInWindow = true;
353     }
354 }
355
356 void Widget::afterMouseDown(NSView *view, Widget* widget)
357 {
358     if (!widget) {
359         BEGIN_BLOCK_OBJC_EXCEPTIONS;
360         safeRemoveFromSuperview(view);
361         END_BLOCK_OBJC_EXCEPTIONS;
362     } else {
363         ASSERT(widget->m_data->mustStayInWindow);
364         widget->m_data->mustStayInWindow = false;
365         if (widget->m_data->removeFromSuperviewSoon)
366             widget->removeFromSuperview();
367     }
368 }
369
370 // These are here to deal with flipped coords on Mac.
371 IntRect Widget::convertFromRootToContainingWindow(const Widget* rootWidget, const IntRect& rect)
372 {
373     if (!rootWidget->platformWidget())
374         return rect;
375
376     BEGIN_BLOCK_OBJC_EXCEPTIONS;
377     return enclosingIntRect([rootWidget->platformWidget() convertRect:rect toView:nil]);
378     END_BLOCK_OBJC_EXCEPTIONS;
379
380     return rect;
381 }
382
383 IntRect Widget::convertFromContainingWindowToRoot(const Widget* rootWidget, const IntRect& rect)
384 {
385     if (!rootWidget->platformWidget())
386         return rect;
387
388     BEGIN_BLOCK_OBJC_EXCEPTIONS;
389     return enclosingIntRect([rootWidget->platformWidget() convertRect:rect fromView:nil]);
390     END_BLOCK_OBJC_EXCEPTIONS;
391
392     return rect;
393 }
394
395 IntPoint Widget::convertFromRootToContainingWindow(const Widget* rootWidget, const IntPoint& point)
396 {
397     if (!rootWidget->platformWidget())
398         return point;
399
400     BEGIN_BLOCK_OBJC_EXCEPTIONS;
401     return IntPoint([rootWidget->platformWidget() convertPoint:point toView:nil]);
402     END_BLOCK_OBJC_EXCEPTIONS;
403     return point;
404 }
405
406 IntPoint Widget::convertFromContainingWindowToRoot(const Widget* rootWidget, const IntPoint& point)
407 {
408     if (!rootWidget->platformWidget())
409         return point;
410
411     BEGIN_BLOCK_OBJC_EXCEPTIONS;
412     return IntPoint([rootWidget->platformWidget() convertPoint:point fromView:nil]);
413     END_BLOCK_OBJC_EXCEPTIONS;
414
415     return point;
416 }
417
418 NSView *Widget::platformWidget() const
419 {
420     return m_widget.get();
421 }
422
423 void Widget::setPlatformWidget(NSView *widget)
424 {
425     if (widget == m_widget)
426         return;
427
428     m_widget = widget;
429     m_data->previousVisibleRect = NSZeroRect;
430 }
431
432 } // namespace WebCore