1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #import "ui/base/cocoa/underlay_opengl_hosting_window.h" 6 7 #import <objc/runtime.h> 8 9 #include "base/logging.h" 10 #include "base/mac/mac_util.h" 11 #include "base/mac/scoped_nsautorelease_pool.h" 12 #include "base/mac/scoped_nsobject.h" 13 14 @interface NSWindow (UndocumentedAPI) 15 // Normally, punching a hole in a window by painting a subview with a 16 // transparent color causes the shadow for that area to also not be present. 17 // That feature is "content has shadow", which means that shadows are effective 18 // even in the content area of the window. If, however, "content has shadow" is 19 // turned off, then the transparent area of the content casts a shadow. The one 20 // tricky part is that even if "content has shadow" is turned off, "the content" 21 // is defined as being the scanline from the leftmost opaque part to the 22 // rightmost opaque part. Therefore, to force the entire window to have a 23 // shadow, make sure that for the entire content region, there is an opaque area 24 // on the right and left edge of the window. 25 - (void)_setContentHasShadow:(BOOL)shadow; 26 @end 27 28 @interface OpaqueView : NSView 29 @end 30 31 namespace { 32 33 NSComparisonResult OpaqueViewsOnTop(id view1, id view2, void* context) { 34 BOOL view_1_is_opaque_view = [view1 isKindOfClass:[OpaqueView class]]; 35 BOOL view_2_is_opaque_view = [view2 isKindOfClass:[OpaqueView class]]; 36 if (view_1_is_opaque_view && view_2_is_opaque_view) 37 return NSOrderedSame; 38 if (view_1_is_opaque_view) 39 return NSOrderedDescending; 40 if (view_2_is_opaque_view) 41 return NSOrderedAscending; 42 return NSOrderedSame; 43 } 44 45 } // namespace 46 47 @implementation OpaqueView 48 49 - (void)drawRect:(NSRect)r { 50 [[NSColor blackColor] set]; 51 NSRectFill(r); 52 } 53 54 - (void)resetCursorRects { 55 // When a view is moved relative to its peers, its cursor rects are reset. 56 // (This is an undocumented side-effect.) At that time, verify that any 57 // OpaqueViews are z-ordered in the front, and enforce it if need be. 58 59 NSView* rootView = [self superview]; 60 DCHECK_EQ((NSView*)nil, [rootView superview]); 61 NSArray* subviews = [rootView subviews]; 62 63 // If a window has any opaques, it will have exactly two. 64 DCHECK_EQ(2U, [[subviews indexesOfObjectsPassingTest: 65 ^(id el, NSUInteger i, BOOL *stop) { 66 return [el isKindOfClass:[OpaqueView class]]; 67 }] count]); 68 69 NSUInteger count = [subviews count]; 70 if (count < 2) 71 return; 72 73 if (![[subviews objectAtIndex:count - 1] isKindOfClass:[OpaqueView class]] || 74 ![[subviews objectAtIndex:count - 2] isKindOfClass:[OpaqueView class]]) { 75 // Do not sort the subviews array here and call -[NSView setSubviews:] as 76 // that causes a crash on 10.6. 77 [rootView sortSubviewsUsingFunction:OpaqueViewsOnTop context:NULL]; 78 } 79 } 80 81 @end 82 83 @implementation UnderlayOpenGLHostingWindow 84 85 - (id)initWithContentRect:(NSRect)contentRect 86 styleMask:(NSUInteger)windowStyle 87 backing:(NSBackingStoreType)bufferingType 88 defer:(BOOL)deferCreation { 89 // It is invalid to create windows with zero width or height. It screws things 90 // up royally: 91 // - It causes console spew: <http://crbug.com/78973> 92 // - It breaks Expose: <http://sourceforge.net/projects/heat-meteo/forums/forum/268087/topic/4582610> 93 // 94 // This is a banned practice 95 // <http://www.chromium.org/developers/coding-style/cocoa-dos-and-donts>. Do 96 // not do this. Use kWindowSizeDeterminedLater in 97 // ui/base/cocoa/window_size_constants.h instead. 98 // 99 // (This is checked here because UnderlayOpenGLHostingWindow is the base of 100 // most Chromium windows, not because this is related to its functionality.) 101 DCHECK(!NSIsEmptyRect(contentRect)); 102 if ((self = [super initWithContentRect:contentRect 103 styleMask:windowStyle 104 backing:bufferingType 105 defer:deferCreation])) { 106 // OpenGL-accelerated content works by punching holes in windows. Therefore 107 // all windows hosting OpenGL content must not be opaque. 108 [self setOpaque:NO]; 109 110 if (windowStyle & NSTitledWindowMask) { 111 // Only fiddle with shadows if the window is a proper window with a 112 // title bar and all. 113 [self _setContentHasShadow:NO]; 114 115 NSView* rootView = [[self contentView] superview]; 116 const NSRect rootBounds = [rootView bounds]; 117 118 // On 10.7/8, the bottom corners of the window are rounded by magic at a 119 // deeper level than the NSThemeFrame, so it is OK to have the opaques 120 // go all the way to the bottom. 121 const CGFloat kTopEdgeInset = 16; 122 const CGFloat kAlphaValueJustOpaqueEnough = 0.005; 123 124 base::scoped_nsobject<NSView> leftOpaque([[OpaqueView alloc] 125 initWithFrame:NSMakeRect(NSMinX(rootBounds), 126 NSMinY(rootBounds), 127 1, 128 NSHeight(rootBounds) - kTopEdgeInset)]); 129 [leftOpaque setAutoresizingMask:NSViewMaxXMargin | 130 NSViewHeightSizable]; 131 [leftOpaque setAlphaValue:kAlphaValueJustOpaqueEnough]; 132 [rootView addSubview:leftOpaque]; 133 134 base::scoped_nsobject<NSView> rightOpaque([[OpaqueView alloc] 135 initWithFrame:NSMakeRect(NSMaxX(rootBounds) - 1, 136 NSMinY(rootBounds), 137 1, 138 NSHeight(rootBounds) - kTopEdgeInset)]); 139 [rightOpaque setAutoresizingMask:NSViewMinXMargin | 140 NSViewHeightSizable]; 141 [rightOpaque setAlphaValue:kAlphaValueJustOpaqueEnough]; 142 [rootView addSubview:rightOpaque]; 143 } 144 } 145 146 return self; 147 } 148 149 @end 150