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 "chrome/browser/ui/cocoa/custom_frame_view.h" 6 7#import <Carbon/Carbon.h> 8#include <crt_externs.h> 9#import <objc/runtime.h> 10#include <string.h> 11 12#include "base/logging.h" 13#include "base/mac/mac_util.h" 14#include "base/mac/scoped_nsautorelease_pool.h" 15 16namespace { 17BOOL gCanDrawTitle = NO; 18BOOL gCanGetCornerRadius = NO; 19} // namespace 20 21@interface NSView (Swizzles) 22- (void)drawRectOriginal:(NSRect)rect; 23- (NSPoint)_fullScreenButtonOriginOriginal; 24@end 25 26@interface NSWindow (FramedBrowserWindow) 27- (NSPoint)fullScreenButtonOriginAdjustment; 28@end 29 30@implementation NSWindow (CustomFrameView) 31- (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view { 32 [view drawRectOriginal:rect]; 33} 34@end 35 36@interface CustomFrameView : NSView 37 38@end 39 40@implementation CustomFrameView 41 42// This is where we swizzle drawRect, and add in two methods that we 43// need. If any of these fail it shouldn't affect the functionality of the 44// others. If they all fail, we will lose window frame theming and 45// roll overs for our close widgets, but things should still function 46// correctly. 47+ (void)load { 48 // Swizzling should only happen in the browser process. Interacting with 49 // AppKit will run +[borderViewClass initialize] in the renderer, which 50 // may establish Mach IPC with com.apple.windowserver. 51 // Note that CommandLine has not been initialized yet, since this is running 52 // as a module initializer. 53 const char* const* const argv = *_NSGetArgv(); 54 const int argc = *_NSGetArgc(); 55 const char kType[] = "--type="; 56 for (int i = 1; i < argc; ++i) { 57 const char* arg = argv[i]; 58 if (strncmp(arg, kType, strlen(kType)) == 0) 59 return; 60 } 61 62 base::mac::ScopedNSAutoreleasePool pool; 63 64 // On 10.8+ the background for textured windows are no longer drawn by 65 // NSGrayFrame, and NSThemeFrame is used instead <http://crbug.com/114745>. 66 Class borderViewClass = NSClassFromString( 67 base::mac::IsOSMountainLionOrLater() ? @"NSThemeFrame" : @"NSGrayFrame"); 68 DCHECK(borderViewClass); 69 if (!borderViewClass) return; 70 71 // Exchange draw rect. 72 Method m0 = class_getInstanceMethod([self class], @selector(drawRect:)); 73 DCHECK(m0); 74 if (m0) { 75 BOOL didAdd = class_addMethod(borderViewClass, 76 @selector(drawRectOriginal:), 77 method_getImplementation(m0), 78 method_getTypeEncoding(m0)); 79 DCHECK(didAdd); 80 if (didAdd) { 81 Method m1 = class_getInstanceMethod(borderViewClass, 82 @selector(drawRect:)); 83 Method m2 = class_getInstanceMethod(borderViewClass, 84 @selector(drawRectOriginal:)); 85 DCHECK(m1 && m2); 86 if (m1 && m2) { 87 method_exchangeImplementations(m1, m2); 88 } 89 } 90 } 91 92 // Swizzle the method that sets the origin for the Lion fullscreen button. Do 93 // nothing if it cannot be found. 94 m0 = class_getInstanceMethod([self class], 95 @selector(_fullScreenButtonOrigin)); 96 if (m0) { 97 BOOL didAdd = class_addMethod(borderViewClass, 98 @selector(_fullScreenButtonOriginOriginal), 99 method_getImplementation(m0), 100 method_getTypeEncoding(m0)); 101 if (didAdd) { 102 Method m1 = class_getInstanceMethod(borderViewClass, 103 @selector(_fullScreenButtonOrigin)); 104 Method m2 = class_getInstanceMethod(borderViewClass, 105 @selector(_fullScreenButtonOriginOriginal)); 106 if (m1 && m2) { 107 method_exchangeImplementations(m1, m2); 108 } 109 } 110 } 111} 112 113+ (BOOL)canDrawTitle { 114 return gCanDrawTitle; 115} 116 117+ (BOOL)canGetCornerRadius { 118 return gCanGetCornerRadius; 119} 120 121- (id)initWithFrame:(NSRect)frame { 122 // This class is not for instantiating. 123 [self doesNotRecognizeSelector:_cmd]; 124 return nil; 125} 126 127- (id)initWithCoder:(NSCoder*)coder { 128 // This class is not for instantiating. 129 [self doesNotRecognizeSelector:_cmd]; 130 return nil; 131} 132 133// Here is our custom drawing for our frame. 134- (void)drawRect:(NSRect)rect { 135 // Delegate drawing to the window, whose default implementation (above) is to 136 // call into the original implementation. 137 [[self window] drawCustomFrameRect:rect forView:self]; 138} 139 140// Override to move the fullscreen button to the left of the profile avatar. 141- (NSPoint)_fullScreenButtonOrigin { 142 NSWindow* window = [self window]; 143 NSPoint offset = NSZeroPoint; 144 145 if ([window respondsToSelector:@selector(fullScreenButtonOriginAdjustment)]) 146 offset = [window fullScreenButtonOriginAdjustment]; 147 148 NSPoint origin = [self _fullScreenButtonOriginOriginal]; 149 origin.x += offset.x; 150 origin.y += offset.y; 151 return origin; 152} 153 154@end 155