summaryrefslogtreecommitdiffstats
path: root/ui/base/cocoa/underlay_opengl_hosting_window.mm
blob: 8d96f36b79f7988f3592c7a5b6c4bfb237482590 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ui/base/cocoa/underlay_opengl_hosting_window.h"

#import <objc/runtime.h>

#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/memory/scoped_nsobject.h"

@interface NSWindow (UndocumentedAPI)
// Normally, punching a hole in a window by painting a subview with a
// transparent color causes the shadow for that area to also not be present.
// That feature is "content has shadow", which means that shadows are effective
// even in the content area of the window. If, however, "content has shadow" is
// turned off, then the transparent area of the content casts a shadow. The one
// tricky part is that even if "content has shadow" is turned off, "the content"
// is defined as being the scanline from the leftmost opaque part to the
// rightmost opaque part.  Therefore, to force the entire window to have a
// shadow, make sure that for the entire content region, there is an opaque area
// on the right and left edge of the window.
- (void)_setContentHasShadow:(BOOL)shadow;
@end

@interface OpaqueView : NSView
@end

@implementation OpaqueView
- (void)drawRect:(NSRect)r {
  [[NSColor blackColor] set];
  NSRectFill(r);
}
@end

namespace {

NSComparisonResult OpaqueViewsOnTop(id view1, id view2, void* context) {
  BOOL view_1_is_opaque_view = [view1 isKindOfClass:[OpaqueView class]];
  BOOL view_2_is_opaque_view = [view2 isKindOfClass:[OpaqueView class]];
  if (view_1_is_opaque_view && view_2_is_opaque_view)
    return NSOrderedSame;
  if (view_1_is_opaque_view)
    return NSOrderedDescending;
  if (view_2_is_opaque_view)
    return NSOrderedAscending;
  return NSOrderedSame;
}

void RootDidAddSubview(id self, SEL _cmd, NSView* subview) {
  if (![[self window] isKindOfClass:[UnderlayOpenGLHostingWindow class]])
    return;

  // Make sure the opaques are on top.
  [self sortSubviewsUsingFunction:OpaqueViewsOnTop context:NULL];
}

}  // namespace

@implementation UnderlayOpenGLHostingWindow

+ (void)load {
  base::mac::ScopedNSAutoreleasePool pool;

  // On 10.8+ the background for textured windows are no longer drawn by
  // NSGrayFrame, and NSThemeFrame is used instead <http://crbug.com/114745>.
  Class borderViewClass = NSClassFromString(
      base::mac::IsOSMountainLionOrLater() ? @"NSThemeFrame" : @"NSGrayFrame");
  DCHECK(borderViewClass);
  if (!borderViewClass) return;

  // Install callback for added views.
  Method m = class_getInstanceMethod([NSView class], @selector(didAddSubview:));
  DCHECK(m);
  if (m) {
    BOOL didAdd = class_addMethod(borderViewClass,
                                  @selector(didAddSubview:),
                                  reinterpret_cast<IMP>(&RootDidAddSubview),
                                  method_getTypeEncoding(m));
    DCHECK(didAdd);
  }
}

- (id)initWithContentRect:(NSRect)contentRect
                styleMask:(NSUInteger)windowStyle
                  backing:(NSBackingStoreType)bufferingType
                    defer:(BOOL)deferCreation {
  // It is invalid to create windows with zero width or height. It screws things
  // up royally:
  // - It causes console spew: <http://crbug.com/78973>
  // - It breaks Expose: <http://sourceforge.net/projects/heat-meteo/forums/forum/268087/topic/4582610>
  //
  // This is a banned practice
  // <http://www.chromium.org/developers/coding-style/cocoa-dos-and-donts>. Do
  // not do this. Use kWindowSizeDeterminedLater in
  // ui/base/cocoa/window_size_constants.h instead.
  //
  // (This is checked here because UnderlayOpenGLHostingWindow is the base of
  // most Chromium windows, not because this is related to its functionality.)
  DCHECK(contentRect.size.width > 0 &&
         contentRect.size.height > 0);
  if ((self = [super initWithContentRect:contentRect
                               styleMask:windowStyle
                                 backing:bufferingType
                                   defer:deferCreation])) {
    if (base::mac::IsOSSnowLeopardOrLater()) {
      // Hole punching is used when IOSurfaces are used to transport, which is
      // only on > 10.5. Therefore, on > 10.5, the window must always be
      // non-opaque whether or not it's titled if we want hole punching to work.
      [self setOpaque:NO];

      if (windowStyle & NSTitledWindowMask) {
        // Only fiddle with shadows if the window is a proper window with a
        // title bar and all. (The invisible opaque area technique only works on
        // > 10.5, but that is guaranteed by this point.)
        [self _setContentHasShadow:NO];

        NSView* rootView = [[self contentView] superview];
        const NSRect rootBounds = [rootView bounds];

        const CGFloat kEdgeInset = 16;
        const CGFloat kAlphaValueJustOpaqueEnough = 0.002;

        scoped_nsobject<NSView> leftOpaque([[OpaqueView alloc] initWithFrame:
            NSMakeRect(NSMinX(rootBounds), NSMinY(rootBounds) + kEdgeInset,
                       1, NSHeight(rootBounds) - 2 * kEdgeInset)]);
        [leftOpaque setAutoresizingMask:NSViewMaxXMargin |
                                        NSViewHeightSizable];
        [leftOpaque setAlphaValue:kAlphaValueJustOpaqueEnough];
        [rootView addSubview:leftOpaque];

        scoped_nsobject<NSView> rightOpaque([[OpaqueView alloc] initWithFrame:
            NSMakeRect(NSMaxX(rootBounds) - 1, NSMinY(rootBounds) + kEdgeInset,
                       1, NSHeight(rootBounds) - 2 * kEdgeInset)]);
        [rightOpaque setAutoresizingMask:NSViewMinXMargin |
                                         NSViewHeightSizable];
        [rightOpaque setAlphaValue:kAlphaValueJustOpaqueEnough];
        [rootView addSubview:rightOpaque];
      }
    }
  }

  return self;
}

@end