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