blob: eddf7f4da1d0e43fc21445e9e28ce0015f547f29 [file] [log] [blame]
// 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