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 63namespace WebCore { 64 65class WidgetPrivate { 66public: 67 WidgetPrivate() 68 : previousVisibleRect(NSZeroRect) 69 { 70 } 71 72 bool mustStayInWindow; 73 bool removeFromSuperviewSoon; 74 NSRect previousVisibleRect; 75}; 76 77static 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 93Widget::Widget(NSView *view) 94 : m_data(new WidgetPrivate) 95{ 96 init(view); 97 m_data->mustStayInWindow = false; 98 m_data->removeFromSuperviewSoon = false; 99} 100 101Widget::~Widget() 102{ 103 delete m_data; 104} 105 106// FIXME: Should move this to Chrome; bad layering that this knows about Frame. 107void 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 130void Widget::setCursor(const Cursor& cursor) 131{ 132 ScrollView* view = root(); 133 if (!view) 134 return; 135 view->hostWindow()->setCursor(cursor); 136} 137 138void 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 150void 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 162IntRect 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 174void 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 199void 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 218NSView *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 232void 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 323void 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 335void 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 347void 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 356void 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. 371IntRect 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 383IntRect 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 395IntPoint 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 406IntPoint 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 418NSView *Widget::platformWidget() const 419{ 420 return m_widget.get(); 421} 422 423void 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 433