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