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 59namespace WebCore { 60 61class WidgetPrivate { 62public: 63 bool mustStayInWindow; 64 bool removeFromSuperviewSoon; 65}; 66 67static 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 83Widget::Widget(NSView *view) 84 : m_data(new WidgetPrivate) 85{ 86 init(view); 87 m_data->mustStayInWindow = false; 88 m_data->removeFromSuperviewSoon = false; 89} 90 91Widget::~Widget() 92{ 93 releasePlatformWidget(); 94 delete m_data; 95} 96 97// FIXME: Should move this to Chrome; bad layering that this knows about Frame. 98void 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 120void 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 132void 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 144IntRect 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 156void 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 173NSView* 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 187void 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 245void 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 256void 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 268void 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 277void 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. 292IntRect 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 304IntRect 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 316IntPoint 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 327IntPoint 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 339void Widget::releasePlatformWidget() 340{ 341 HardRelease(m_widget); 342} 343 344void Widget::retainPlatformWidget() 345{ 346 HardRetain(m_widget); 347} 348 349} // namespace WebCore 350 351