/* * Copyright (C) 2004, 2005, 2006, 2008, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "Widget.h" #ifdef BUILDING_ON_TIGER #import "AutodrainedPool.h" #endif #import "BlockExceptions.h" #import "Chrome.h" #import "Cursor.h" #import "Document.h" #import "Font.h" #import "Frame.h" #import "GraphicsContext.h" #import "NotImplemented.h" #import "Page.h" #import "PlatformMouseEvent.h" #import "ScrollView.h" #import "WebCoreFrameView.h" #import "WebCoreView.h" #import @interface NSWindow (WebWindowDetails) - (BOOL)_needsToResetDragMargins; - (void)_setNeedsToResetDragMargins:(BOOL)needs; @end @interface NSView (WebSetSelectedMethods) - (void)setIsSelected:(BOOL)isSelected; - (void)webPlugInSetIsSelected:(BOOL)isSelected; @end @interface NSView (Widget) - (void)visibleRectDidChange; @end namespace WebCore { class WidgetPrivate { public: WidgetPrivate() : previousVisibleRect(NSZeroRect) { } bool mustStayInWindow; bool removeFromSuperviewSoon; NSRect previousVisibleRect; }; static void safeRemoveFromSuperview(NSView *view) { // If the the view is the first responder, then set the window's first responder to nil so // we don't leave the window pointing to a view that's no longer in it. NSWindow *window = [view window]; NSResponder *firstResponder = [window firstResponder]; if ([firstResponder isKindOfClass:[NSView class]] && [(NSView *)firstResponder isDescendantOf:view]) [window makeFirstResponder:nil]; // Suppress the resetting of drag margins since we know we can't affect them. BOOL resetDragMargins = [window _needsToResetDragMargins]; [window _setNeedsToResetDragMargins:NO]; [view removeFromSuperview]; [window _setNeedsToResetDragMargins:resetDragMargins]; } Widget::Widget(NSView *view) : m_data(new WidgetPrivate) { init(view); m_data->mustStayInWindow = false; m_data->removeFromSuperviewSoon = false; } Widget::~Widget() { delete m_data; } // FIXME: Should move this to Chrome; bad layering that this knows about Frame. void Widget::setFocus(bool focused) { if (!focused) return; Frame* frame = Frame::frameForWidget(this); if (!frame) return; BEGIN_BLOCK_OBJC_EXCEPTIONS; NSView *view = [platformWidget() _webcore_effectiveFirstResponder]; if (Page* page = frame->page()) page->chrome()->focusNSView(view); END_BLOCK_OBJC_EXCEPTIONS; } void Widget::setCursor(const Cursor& cursor) { ScrollView* view = root(); if (!view) return; view->hostWindow()->setCursor(cursor); } void Widget::show() { if (isSelfVisible()) return; setSelfVisible(true); BEGIN_BLOCK_OBJC_EXCEPTIONS; [getOuterView() setHidden:NO]; END_BLOCK_OBJC_EXCEPTIONS; } void Widget::hide() { if (!isSelfVisible()) return; setSelfVisible(false); BEGIN_BLOCK_OBJC_EXCEPTIONS; [getOuterView() setHidden:YES]; END_BLOCK_OBJC_EXCEPTIONS; } IntRect Widget::frameRect() const { if (!platformWidget()) return m_frame; BEGIN_BLOCK_OBJC_EXCEPTIONS; return enclosingIntRect([getOuterView() frame]); END_BLOCK_OBJC_EXCEPTIONS; return m_frame; } void Widget::setFrameRect(const IntRect& rect) { m_frame = rect; BEGIN_BLOCK_OBJC_EXCEPTIONS; NSView *v = getOuterView(); if (!v) return; NSRect visibleRect = [v visibleRect]; NSRect f = rect; if (!NSEqualRects(f, [v frame])) { [v setFrame:f]; [v setNeedsDisplay:NO]; } else if (!NSEqualRects(visibleRect, m_data->previousVisibleRect) && [v respondsToSelector:@selector(visibleRectDidChange)]) [v visibleRectDidChange]; m_data->previousVisibleRect = visibleRect; END_BLOCK_OBJC_EXCEPTIONS; } NSView* Widget::getOuterView() const { NSView* view = platformWidget(); // If this widget's view is a WebCoreFrameScrollView then we // resize its containing view, a WebFrameView. if ([view conformsToProtocol:@protocol(WebCoreFrameScrollView)]) { view = [view superview]; ASSERT(view); } return view; } void Widget::paint(GraphicsContext* p, const IntRect& r) { if (p->paintingDisabled()) return; NSView *view = getOuterView(); NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; if (currentContext == [[view window] graphicsContext] || ![currentContext isDrawingToScreen]) { // This is the common case of drawing into a window or printing. BEGIN_BLOCK_OBJC_EXCEPTIONS; [view displayRectIgnoringOpacity:[view convertRect:r fromView:[view superview]]]; END_BLOCK_OBJC_EXCEPTIONS; } else { // This is the case of drawing into a bitmap context other than a window backing store. It gets hit beneath // -cacheDisplayInRect:toBitmapImageRep:, and when painting into compositing layers. // Transparent subframes are in fact implemented with scroll views that return YES from -drawsBackground (whenever the WebView // itself is in drawsBackground mode). In the normal drawing code path, the scroll views are never asked to draw the background, // so this is not an issue, but in this code path they are, so the following code temporarily turns background drwaing off. NSView *innerView = platformWidget(); NSScrollView *scrollView = 0; if ([innerView conformsToProtocol:@protocol(WebCoreFrameScrollView)]) { ASSERT([innerView isKindOfClass:[NSScrollView class]]); NSScrollView *scrollView = static_cast(innerView); // -copiesOnScroll will return NO whenever the content view is not fully opaque. if ([scrollView drawsBackground] && ![[scrollView contentView] copiesOnScroll]) [scrollView setDrawsBackground:NO]; else scrollView = 0; } CGContextRef cgContext = p->platformContext(); ASSERT(cgContext == [currentContext graphicsPort]); CGContextSaveGState(cgContext); NSRect viewFrame = [view frame]; NSRect viewBounds = [view bounds]; // Set up the translation and (flipped) orientation of the graphics context. In normal drawing, AppKit does it as it descends down // the view hierarchy. CGContextTranslateCTM(cgContext, viewFrame.origin.x - viewBounds.origin.x, viewFrame.origin.y + viewFrame.size.height + viewBounds.origin.y); CGContextScaleCTM(cgContext, 1, -1); BEGIN_BLOCK_OBJC_EXCEPTIONS; { #ifdef BUILDING_ON_TIGER AutodrainedPool pool; #endif NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]; [view displayRectIgnoringOpacity:[view convertRect:r fromView:[view superview]] inContext:nsContext]; } END_BLOCK_OBJC_EXCEPTIONS; CGContextRestoreGState(cgContext); if (scrollView) [scrollView setDrawsBackground:YES]; } } void Widget::setIsSelected(bool isSelected) { NSView *view = platformWidget(); BEGIN_BLOCK_OBJC_EXCEPTIONS; if ([view respondsToSelector:@selector(webPlugInSetIsSelected:)]) [view webPlugInSetIsSelected:isSelected]; else if ([view respondsToSelector:@selector(setIsSelected:)]) [view setIsSelected:isSelected]; END_BLOCK_OBJC_EXCEPTIONS; } void Widget::removeFromSuperview() { if (m_data->mustStayInWindow) m_data->removeFromSuperviewSoon = true; else { m_data->removeFromSuperviewSoon = false; BEGIN_BLOCK_OBJC_EXCEPTIONS; safeRemoveFromSuperview(getOuterView()); END_BLOCK_OBJC_EXCEPTIONS; } } void Widget::beforeMouseDown(NSView *unusedView, Widget* widget) { if (widget) { ASSERT_UNUSED(unusedView, unusedView == widget->getOuterView()); ASSERT(!widget->m_data->mustStayInWindow); widget->m_data->mustStayInWindow = true; } } void Widget::afterMouseDown(NSView *view, Widget* widget) { if (!widget) { BEGIN_BLOCK_OBJC_EXCEPTIONS; safeRemoveFromSuperview(view); END_BLOCK_OBJC_EXCEPTIONS; } else { ASSERT(widget->m_data->mustStayInWindow); widget->m_data->mustStayInWindow = false; if (widget->m_data->removeFromSuperviewSoon) widget->removeFromSuperview(); } } // These are here to deal with flipped coords on Mac. IntRect Widget::convertFromRootToContainingWindow(const Widget* rootWidget, const IntRect& rect) { if (!rootWidget->platformWidget()) return rect; BEGIN_BLOCK_OBJC_EXCEPTIONS; return enclosingIntRect([rootWidget->platformWidget() convertRect:rect toView:nil]); END_BLOCK_OBJC_EXCEPTIONS; return rect; } IntRect Widget::convertFromContainingWindowToRoot(const Widget* rootWidget, const IntRect& rect) { if (!rootWidget->platformWidget()) return rect; BEGIN_BLOCK_OBJC_EXCEPTIONS; return enclosingIntRect([rootWidget->platformWidget() convertRect:rect fromView:nil]); END_BLOCK_OBJC_EXCEPTIONS; return rect; } IntPoint Widget::convertFromRootToContainingWindow(const Widget* rootWidget, const IntPoint& point) { if (!rootWidget->platformWidget()) return point; BEGIN_BLOCK_OBJC_EXCEPTIONS; return IntPoint([rootWidget->platformWidget() convertPoint:point toView:nil]); END_BLOCK_OBJC_EXCEPTIONS; return point; } IntPoint Widget::convertFromContainingWindowToRoot(const Widget* rootWidget, const IntPoint& point) { if (!rootWidget->platformWidget()) return point; BEGIN_BLOCK_OBJC_EXCEPTIONS; return IntPoint([rootWidget->platformWidget() convertPoint:point fromView:nil]); END_BLOCK_OBJC_EXCEPTIONS; return point; } NSView *Widget::platformWidget() const { return m_widget.get(); } void Widget::setPlatformWidget(NSView *widget) { if (widget == m_widget) return; m_widget = widget; m_data->previousVisibleRect = NSZeroRect; } } // namespace WebCore