• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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