• 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/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
31namespace {
32
33NSComparisonResult 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