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