Take the normal NSView code path when drawing into an inclusive layer
[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
30 #import "BlockExceptions.h"
31 #import "Chrome.h"
32 #import "Cursor.h"
33 #import "Document.h"
34 #import "Font.h"
35 #import "Frame.h"
36 #import "GraphicsContext.h"
37 #import "NotImplemented.h"
38 #import "Page.h"
39 #import "PlatformMouseEvent.h"
40 #import "ScrollView.h"
41 #import "WebCoreFrameView.h"
42 #import "WebCoreView.h"
43 #import <wtf/RetainPtr.h>
44
45 @interface NSWindow (WebWindowDetails)
46 - (BOOL)_needsToResetDragMargins;
47 - (void)_setNeedsToResetDragMargins:(BOOL)needs;
48 @end
49
50 @interface NSView (WebSetSelectedMethods)
51 - (void)setIsSelected:(BOOL)isSelected;
52 - (void)webPlugInSetIsSelected:(BOOL)isSelected;
53 @end
54
55 @interface NSView (Widget)
56 - (void)visibleRectDidChange;
57 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
58 - (BOOL)_hasCanDrawSubviewsIntoLayerOrAncestor;
59 #endif
60 @end
61
62 namespace WebCore {
63
64 class WidgetPrivate {
65 public:
66     WidgetPrivate()
67         : previousVisibleRect(NSZeroRect)
68     {
69     }
70
71     NSRect previousVisibleRect;
72 };
73
74 static void safeRemoveFromSuperview(NSView *view)
75 {
76     // If the the view is the first responder, then set the window's first responder to nil so
77     // we don't leave the window pointing to a view that's no longer in it.
78     NSWindow *window = [view window];
79     NSResponder *firstResponder = [window firstResponder];
80     if ([firstResponder isKindOfClass:[NSView class]] && [(NSView *)firstResponder isDescendantOf:view])
81         [window makeFirstResponder:nil];
82
83     // Suppress the resetting of drag margins since we know we can't affect them.
84     BOOL resetDragMargins = [window _needsToResetDragMargins];
85     [window _setNeedsToResetDragMargins:NO];
86     [view removeFromSuperview];
87     [window _setNeedsToResetDragMargins:resetDragMargins];
88 }
89
90 Widget::Widget(NSView *view)
91     : m_data(new WidgetPrivate)
92 {
93     init(view);
94 }
95
96 Widget::~Widget()
97 {
98     delete m_data;
99 }
100
101 // FIXME: Should move this to Chrome; bad layering that this knows about Frame.
102 void Widget::setFocus(bool focused)
103 {
104     if (!focused)
105         return;
106
107     Frame* frame = Frame::frameForWidget(this);
108     if (!frame)
109         return;
110
111     BEGIN_BLOCK_OBJC_EXCEPTIONS;
112  
113     // Call this even when there is no platformWidget(). WK2 will focus on the widget in the UIProcess.
114     NSView *view = [platformWidget() _webcore_effectiveFirstResponder];
115     if (Page* page = frame->page())
116         page->chrome().focusNSView(view);
117
118     END_BLOCK_OBJC_EXCEPTIONS;
119 }
120
121 void Widget::setCursor(const Cursor& cursor)
122 {
123     ScrollView* view = root();
124     if (!view)
125         return;
126     view->hostWindow()->setCursor(cursor);
127 }
128
129 void Widget::show()
130 {
131     if (isSelfVisible())
132         return;
133
134     setSelfVisible(true);
135
136     BEGIN_BLOCK_OBJC_EXCEPTIONS;
137     [getOuterView() setHidden:NO];
138     END_BLOCK_OBJC_EXCEPTIONS;
139 }
140
141 void Widget::hide()
142 {
143     if (!isSelfVisible())
144         return;
145
146     setSelfVisible(false);
147
148     BEGIN_BLOCK_OBJC_EXCEPTIONS;
149     [getOuterView() setHidden:YES];
150     END_BLOCK_OBJC_EXCEPTIONS;
151 }
152
153 IntRect Widget::frameRect() const
154 {
155     if (!platformWidget())
156         return m_frame;
157
158     BEGIN_BLOCK_OBJC_EXCEPTIONS;
159     return enclosingIntRect([getOuterView() frame]);
160     END_BLOCK_OBJC_EXCEPTIONS;
161     
162     return m_frame;
163 }
164
165 void Widget::setFrameRect(const IntRect& rect)
166 {
167     m_frame = rect;
168
169     BEGIN_BLOCK_OBJC_EXCEPTIONS;
170     NSView *outerView = getOuterView();
171     if (!outerView)
172         return;
173
174     // Take a reference to this Widget, because sending messages to outerView can invoke arbitrary
175     // code, which can deref it.
176     RefPtr<Widget> protectedThis(this);
177
178     NSRect visibleRect = [outerView visibleRect];
179     NSRect f = rect;
180     if (!NSEqualRects(f, [outerView frame])) {
181         [outerView setFrame:f];
182         [outerView setNeedsDisplay:NO];
183     } else if (!NSEqualRects(visibleRect, m_data->previousVisibleRect) && [outerView respondsToSelector:@selector(visibleRectDidChange)])
184         [outerView visibleRectDidChange];
185
186     m_data->previousVisibleRect = visibleRect;
187     END_BLOCK_OBJC_EXCEPTIONS;
188 }
189
190 NSView *Widget::getOuterView() const
191 {
192     NSView *view = platformWidget();
193
194     // If this widget's view is a WebCoreFrameScrollView then we
195     // resize its containing view, a WebFrameView.
196     if ([view conformsToProtocol:@protocol(WebCoreFrameScrollView)]) {
197         view = [view superview];
198         ASSERT(view);
199     }
200
201     return view;
202 }
203
204 void Widget::paint(GraphicsContext* p, const IntRect& r)
205 {
206     if (p->paintingDisabled())
207         return;
208     NSView *view = getOuterView();
209
210     // Take a reference to this Widget, because sending messages to the views can invoke arbitrary
211     // code, which can deref it.
212     RefPtr<Widget> protectedThis(this);
213
214     BOOL hasCanDrawSubviewsIntoLayerOrAncestor = NO;
215 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
216     hasCanDrawSubviewsIntoLayerOrAncestor = [view _hasCanDrawSubviewsIntoLayerOrAncestor];
217 #endif
218     
219     NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
220     if (currentContext == [[view window] graphicsContext] || ![currentContext isDrawingToScreen] || hasCanDrawSubviewsIntoLayerOrAncestor) {
221         // This is the common case of drawing into a window or an inclusive layer, or printing.
222         BEGIN_BLOCK_OBJC_EXCEPTIONS;
223         [view displayRectIgnoringOpacity:[view convertRect:r fromView:[view superview]]];
224         END_BLOCK_OBJC_EXCEPTIONS;
225     } else {
226         // This is the case of drawing into a bitmap context other than a window backing store. It gets hit beneath
227         // -cacheDisplayInRect:toBitmapImageRep:, and when painting into compositing layers.
228
229         // Transparent subframes are in fact implemented with scroll views that return YES from -drawsBackground (whenever the WebView
230         // itself is in drawsBackground mode). In the normal drawing code path, the scroll views are never asked to draw the background,
231         // so this is not an issue, but in this code path they are, so the following code temporarily turns background drwaing off.
232         NSView *innerView = platformWidget();
233         NSScrollView *scrollView = 0;
234         if ([innerView conformsToProtocol:@protocol(WebCoreFrameScrollView)]) {
235             ASSERT([innerView isKindOfClass:[NSScrollView class]]);
236             NSScrollView *scrollView = static_cast<NSScrollView *>(innerView);
237             // -copiesOnScroll will return NO whenever the content view is not fully opaque.
238             if ([scrollView drawsBackground] && ![[scrollView contentView] copiesOnScroll])
239                 [scrollView setDrawsBackground:NO];
240             else
241                 scrollView = 0;
242         }
243
244         CGContextRef cgContext = p->platformContext();
245         ASSERT(cgContext == [currentContext graphicsPort]);
246         CGContextSaveGState(cgContext);
247
248         NSRect viewFrame = [view frame];
249         NSRect viewBounds = [view bounds];
250         // Set up the translation and (flipped) orientation of the graphics context. In normal drawing, AppKit does it as it descends down
251         // the view hierarchy.
252         bool shouldFlipContext = true;
253 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
254         shouldFlipContext = false;
255 #endif
256         if (shouldFlipContext) {
257             CGContextTranslateCTM(cgContext, viewFrame.origin.x - viewBounds.origin.x, viewFrame.origin.y + viewFrame.size.height + viewBounds.origin.y);
258             CGContextScaleCTM(cgContext, 1, -1);
259         } else
260             CGContextTranslateCTM(cgContext, viewFrame.origin.x - viewBounds.origin.x, viewFrame.origin.y + viewBounds.origin.y);
261
262         BEGIN_BLOCK_OBJC_EXCEPTIONS;
263         {
264             NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES];
265             [view displayRectIgnoringOpacity:[view convertRect:r fromView:[view superview]] inContext:nsContext];
266         }
267         END_BLOCK_OBJC_EXCEPTIONS;
268
269         CGContextRestoreGState(cgContext);
270
271         if (scrollView)
272             [scrollView setDrawsBackground:YES];
273     }
274 }
275
276 void Widget::setIsSelected(bool isSelected)
277 {
278     NSView *view = platformWidget();
279
280     BEGIN_BLOCK_OBJC_EXCEPTIONS;
281     if ([view respondsToSelector:@selector(webPlugInSetIsSelected:)])
282         [view webPlugInSetIsSelected:isSelected];
283     else if ([view respondsToSelector:@selector(setIsSelected:)])
284         [view setIsSelected:isSelected];
285     END_BLOCK_OBJC_EXCEPTIONS;
286 }
287
288 void Widget::removeFromSuperview()
289 {
290     BEGIN_BLOCK_OBJC_EXCEPTIONS;
291     safeRemoveFromSuperview(getOuterView());
292     END_BLOCK_OBJC_EXCEPTIONS;
293 }
294
295 // These are here to deal with flipped coords on Mac.
296 IntRect Widget::convertFromRootToContainingWindow(const Widget* rootWidget, const IntRect& rect)
297 {
298     if (!rootWidget->platformWidget())
299         return rect;
300
301     BEGIN_BLOCK_OBJC_EXCEPTIONS;
302     return enclosingIntRect([rootWidget->platformWidget() convertRect:rect toView:nil]);
303     END_BLOCK_OBJC_EXCEPTIONS;
304
305     return rect;
306 }
307
308 IntRect Widget::convertFromContainingWindowToRoot(const Widget* rootWidget, const IntRect& rect)
309 {
310     if (!rootWidget->platformWidget())
311         return rect;
312
313     BEGIN_BLOCK_OBJC_EXCEPTIONS;
314     return enclosingIntRect([rootWidget->platformWidget() convertRect:rect fromView:nil]);
315     END_BLOCK_OBJC_EXCEPTIONS;
316
317     return rect;
318 }
319
320 IntPoint Widget::convertFromRootToContainingWindow(const Widget* rootWidget, const IntPoint& point)
321 {
322     if (!rootWidget->platformWidget())
323         return point;
324
325     BEGIN_BLOCK_OBJC_EXCEPTIONS;
326     return IntPoint([rootWidget->platformWidget() convertPoint:point toView:nil]);
327     END_BLOCK_OBJC_EXCEPTIONS;
328     return point;
329 }
330
331 IntPoint Widget::convertFromContainingWindowToRoot(const Widget* rootWidget, const IntPoint& point)
332 {
333     if (!rootWidget->platformWidget())
334         return point;
335
336     BEGIN_BLOCK_OBJC_EXCEPTIONS;
337     return IntPoint([rootWidget->platformWidget() convertPoint:point fromView:nil]);
338     END_BLOCK_OBJC_EXCEPTIONS;
339
340     return point;
341 }
342
343 NSView *Widget::platformWidget() const
344 {
345     return m_widget.get();
346 }
347
348 void Widget::setPlatformWidget(NSView *widget)
349 {
350     if (widget == m_widget)
351         return;
352
353     m_widget = widget;
354     m_data->previousVisibleRect = NSZeroRect;
355 }
356
357 } // namespace WebCore