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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
// Copyright 2014 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 "chrome/browser/ui/cocoa/full_size_content_window.h"
#include <crt_externs.h>
#include "base/auto_reset.h"
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_objc_class_swizzler.h"
@interface FullSizeContentWindow ()
+ (BOOL)shouldUseFullSizeContentViewForStyle:(NSUInteger)windowStyle;
@end
// This view always takes the size of its superview. It is intended to be used
// as a NSWindow's contentView. It is needed because NSWindow's implementation
// explicitly resizes the contentView at inopportune times.
@interface FullSizeContentView : NSView {
BOOL forceFrameFlag_;
}
// This method allows us to set the content view size since setFrameSize is
// overridden to prevent the view from shrinking.
- (void)forceFrame:(NSRect)frame;
@end
@implementation FullSizeContentView
// This method is directly called by AppKit during a live window resize.
// Override it to prevent the content view from shrinking.
- (void)setFrameSize:(NSSize)size {
if ([self superview] && !forceFrameFlag_)
size = [[self superview] bounds].size;
[super setFrameSize:size];
}
- (void)forceFrame:(NSRect)frame {
forceFrameFlag_ = YES;
[super setFrame:frame];
forceFrameFlag_ = NO;
}
@end
static bool g_disable_callstacksymbols = false;
static IMP g_original_callstacksymbols_implementation;
@interface FullSizeContentWindowSwizzlingSupport : NSObject
@end
@implementation FullSizeContentWindowSwizzlingSupport
// This method replaces [NSThread callStackSymbols] via swizzling - see +load
// below.
+ (NSArray*)callStackSymbols {
return g_disable_callstacksymbols ?
@[@"+callStackSymbols disabled for performance reasons"] :
g_original_callstacksymbols_implementation(
self, @selector(callStackSymbols));
}
@end
@implementation FullSizeContentWindow
#pragma mark - Lifecycle
// In initWithContentRect:styleMask:backing:defer:, the call to
// [NSView addSubview:positioned:relativeTo:] causes NSWindow to complain that
// an unknown view is being added to it, and to generate a stack trace.
// Not only does this stack trace pollute the console, it can also take hundreds
// of milliseconds to generate (because of symbolication). By swizzling
// [NSThread callStackSymbols] we can prevent the stack trace output.
// See crbug.com/520373 .
+ (void)load {
// Swizzling should only happen in the browser process.
const char* const* const argv = *_NSGetArgv();
const int argc = *_NSGetArgc();
const char kType[] = "--type=";
for (int i = 1; i < argc; ++i) {
const char* arg = argv[i];
if (strncmp(arg, kType, strlen(kType)) == 0) {
return;
}
}
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class targetClass = [NSThread class];
Class swizzleClass = [FullSizeContentWindowSwizzlingSupport class];
SEL targetSelector = @selector(callStackSymbols);
CR_DEFINE_STATIC_LOCAL(base::mac::ScopedObjCClassSwizzler,
callStackSymbolsSuppressor, (targetClass,
swizzleClass, targetSelector));
g_original_callstacksymbols_implementation =
callStackSymbolsSuppressor.GetOriginalImplementation();
});
}
- (instancetype)init {
NOTREACHED();
return nil;
}
- (instancetype)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)windowStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)deferCreation {
return [self initWithContentRect:contentRect
styleMask:windowStyle
backing:bufferingType
defer:deferCreation
wantsViewsOverTitlebar:NO];
}
- (instancetype)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)windowStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)deferCreation
wantsViewsOverTitlebar:(BOOL)wantsViewsOverTitlebar {
self = [super initWithContentRect:contentRect
styleMask:windowStyle
backing:bufferingType
defer:deferCreation];
if (self) {
if (wantsViewsOverTitlebar &&
[FullSizeContentWindow
shouldUseFullSizeContentViewForStyle:windowStyle]) {
chromeWindowView_.reset([[FullSizeContentView alloc] init]);
[chromeWindowView_
setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[self setContentView:chromeWindowView_];
[chromeWindowView_ setFrame:[[chromeWindowView_ superview] bounds]];
// Our content view overlaps the window control buttons, so we must ensure
// it is positioned below the buttons.
NSView* superview = [chromeWindowView_ superview];
[chromeWindowView_ removeFromSuperview];
// Prevent the AppKit from generating a backtrace to include in it's
// complaint about our upcoming call to addSubview:positioned:relativeTo:.
// See +load for more info.
base::AutoReset<bool> disable_symbolication(&g_disable_callstacksymbols,
true);
[superview addSubview:chromeWindowView_
positioned:NSWindowBelow
relativeTo:nil];
}
}
return self;
}
- (void)forceContentViewFrame:(NSRect)frame {
if ([chromeWindowView_ isKindOfClass:[FullSizeContentView class]]) {
FullSizeContentView* contentView =
base::mac::ObjCCast<FullSizeContentView>(chromeWindowView_);
[contentView forceFrame:frame];
} else if (chromeWindowView_) {
[chromeWindowView_ setFrame:frame];
} else {
[self.contentView setFrame:frame];
}
}
#pragma mark - Private Methods
+ (BOOL)shouldUseFullSizeContentViewForStyle:(NSUInteger)windowStyle {
return windowStyle & NSTitledWindowMask;
}
#pragma mark - NSWindow Overrides
+ (NSRect)frameRectForContentRect:(NSRect)cRect styleMask:(NSUInteger)aStyle {
if ([self shouldUseFullSizeContentViewForStyle:aStyle])
return cRect;
return [super frameRectForContentRect:cRect styleMask:aStyle];
}
- (NSRect)frameRectForContentRect:(NSRect)contentRect {
if (chromeWindowView_)
return contentRect;
return [super frameRectForContentRect:contentRect];
}
+ (NSRect)contentRectForFrameRect:(NSRect)fRect styleMask:(NSUInteger)aStyle {
if ([self shouldUseFullSizeContentViewForStyle:aStyle])
return fRect;
return [super contentRectForFrameRect:fRect styleMask:aStyle];
}
- (NSRect)contentRectForFrameRect:(NSRect)frameRect {
if (chromeWindowView_)
return frameRect;
return [super contentRectForFrameRect:frameRect];
}
@end
|