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